2019.06.26 23:43


아래 에러 발생시에 해결 방법

 java.lang.RuntimeException: com.android.builder.dexing.DexArchiveMergerException: Error while merging dex archives: 

Caused by: java.lang.RuntimeException: com.android.builder.dexing.DexArchiveMergerException: Error while merging dex archives: 


java.lang.RuntimeException: com.android.builder.dexing.DexArchiveMergerException: Error while merging dex archives:	
com.android.builder.dexing.DexArchiveMergerException: Error while merging dex archives:	
com.android.tools.r8.CompilationFailedException: Compilation failed to complete	
com.android.tools.r8.utils.AbortException: Error: Program type already present: com.android.vending.billing.IInAppBillingService	

 

해결 방법: 

 

multidex용 앱 구성

multidex 구성을 사용하도록 앱 프로젝트를 설정하려면 앱이 지원하는 최소 Android 버전에 따라 앱 프로젝트에서 다음 내용을 변경해야 할 수 있습니다.

minSdkVersion이 21 이상으로 설정되어 있을 경우 아래와 같이 모듈 수준의 build.gradle 파일에서 multiDexEnabled를 true로 설정하기만 하면 됩니다.

android {
    defaultConfig
{
       
...
        minSdkVersion
21
        targetSdkVersion
28
       
multiDexEnabled true
   
}
   
...
}

 

그러나 minSdkVersion이 20 이하로 설정되어 있으면 다음과 같이 multidex 지원 라이브러리를 사용해야 합니다.

  • multidex를 활성화하고 multidex 라이브러리를 종속성으로 추가할 수 있도록 아래와 같이 모듈 수준 build.gradle 파일을 변경합니다.

    android {
        defaultConfig
    {
           
    ...
            minSdkVersion
    15
            targetSdkVersion
    28
           
    multiDexEnabled true
       
    }
       
    ...
    }

    dependencies
    {
     
    compile 'com.android.support:multidex:1.0.3'
    }

     

  • Application 클래스 재정의 여부에 따라 다음 중 하나를 수행합니다.
    • Application 클래스를 재정의하지 않을 경우 매니페스트 파일을 편집하여 <application> 태그에서 android:name을 다음과 같이 설정합니다.

      <?xml version="1.0" encoding="utf-8"?>
      <manifest xmlns:android="http://schemas.android.com/apk/res/android"
         
      package="com.example.myapp">
         
      <application
                 
      android:name="android.support.multidex.MultiDexApplication" >
              ...
         
      </application>
      </manifest>

       

    • Application 클래스를 재정의할 경우 다음과 같이 MultiDexApplication을 확장하도록 변경합니다(해당할 경우).

      public class MyApplication extends MultiDexApplication { ... }

       

    • 또는 Application 클래스를 재정의하지만 기본 클래스를 변경할 수 없을 경우 attachBaseContext() 메서드를 재정의하고 MultiDex.install(this)을 호출하여 multidex를 활성화합니다.

      public class MyApplication extends SomeOtherApplication {
       
      @Override
       
      protected void attachBaseContext(Context base) {
           
      super.attachBaseContext(base);
           
      MultiDex.install(this);
       
      }
      }

       

앱을 빌드할 때 Android 빌드 도구는 기본 DEX 파일(classes.dex)과 지원하는 DEX 파일(classes2.dex, classes3.dex 등)을 필요에 따라 구성합니다. 그 후 빌드 시스템이 모든 DEX 파일을 APK로 패키징합니다.

런타임에서 multidex API는 특수 클래스 로더를 사용하여 (기본 classes.dex 파일)에서만 검색하는 대신) 메서드에서 사용할 수 있는 모든 DEX 파일을 검색합니다.

multidex 지원 라이브러리의 제한사항

multidex 지원 라이브러리에는 몇 가지 알려진 제한사항이 있으며, 이 라이브러리를 앱 빌드 구성에 통합할 때 이러한 제한사항을 파악하고 테스트해야 합니다.

  • 시작 중에 기기 데이터 파티션에 DEX 파일을 설치하는 작업은 복잡하며, 보조 DEX 파일이 큰 경우 ANR(Application Not Responding) 오류가 발생할 수 있습니다. 이 경우, ProGuard로 코드 축소를 적용하여 DEX 파일의 크기를 최소화하고 미사용 코드를 제거해야 합니다.
  • Dalvik linearAlloc 버그(Issue 22586) 때문에, Android 4.0(API 레벨 14) 미만의 플랫폼 버전이 실행 중인 기기에서는 multidex를 사용하는 앱이 시작되지 않을 수도 있습니다. API 레벨 14 미만을 대상으로 하는 경우, 시작 시나 특정 클래스 그룹이 로드될 때 앱에 문제가 있을 수 있으므로, 이러한 플랫폼 버전에서 테스트를 수행해야 합니다. 코드 축소는 이러한 잠재적 문제들을 줄이거나 완전히 없앨 수도 있습니다.
  • multidex 구성을 사용하는 앱이 매우 큰 메모리 할당을 요청하는 경우에는, Dalvik linearAlloc 제한(Issue 78035)으로 인해 런타임 중에 이 애플리케이션이 다운될 수도 있습니다. Android 4.0(API 레벨 14)에서는 할당 제한이 늘어났지만, Android 5.0(API 레벨 21) 미만의 Android 버전에서는 앱이 이 제한에 걸릴 수도 있습니다.

참고 : https://developer.android.com/studio/build/multidex.html?hl=ko

 



Posted by injunech
2019.06.25 22:05


Activity 전환시 다양한 Handler 에서 Activity 호출을 해주는 경우 Activity 의 Stack이 꼬이거나 중복 실행되서  백버튼 눌러도 같은 액티비티가 나올때가 있습니다. 이러한 것을 막아주어서 중복된 Activity 가 Stack상에 쌓이지 않게 막아주는 설정입니다.

AndroidManifest.xml

<activity android:name=".MainActivity" android:launchMode="singleTop">

위와 같이 android:launchMode="singleTop" 추가 해주면 중복 실행을 방지 할 수 있습니다.



Posted by injunech
2019.06.16 22:59


Context is removed when activity called onDestroy().

So when after called context, It goes to error.

So please reference next solution code.

 

# Example Resolution Code

if ( context instanceof Activity ) {
    Activity activity = (Activity)context;
    if ( activity.isFinishing() ) {
        return;
    }
}
Toast.makeText(context, "I'll do things here that depend on my context and views being valid", Toast.LENGTH_SHORT).show();

 

 

#Reference Link

https://stackoverflow.com/questions/7856103/how-can-i-tell-if-my-context-is-still-valid



Posted by injunech
2019.05.15 02:03


1.새로고침하고자 하는 Activity의 Context를 전역변수로 만들어 준다.

public static Context CONTEXT;

 

2.onCreate 부분에서 Context의 값을 지정해준다.

 CONTEXT = this; 

 

3.다른 Activity에서 위의 Context의 onResume() 메서드를 호출한다

 ((ListActivity)ListActivity.CONTEXT).onResume(); 

 

※ 해당 내용은 onResume() 메서드를 통해 새로고침한다는 가정하에 작성한 내용.

onResume() 메서드에 새로고침에 관한 내용이 있어야 한다.

@Override
public void onResume() {
   super.onResume();
   
   ListView.notifyDataSetChanged();
   
}


Posted by injunech
2019.02.13 00:48


[Android] calling ui thread from worker thread



Method compress must be called from the worker thread, currently inferred thread is UI thread less... (Ctrl+F1)  

Inspection info:Ensures that a method which expects to be called on a specific thread, is actually called from that thread. For example, calls on methods in widgets should always be made on the UI thread


위와 같이 View와 같은 Class내에서 Bitmap Compress 함수 사용시

Activity의 context 의 runOnUiThread 에서 수행해주도록 아래와 같이 구현한다.


public static Bitmap croppedBitmap;
public static File croppedFile;

File sdCard = Environment.getExternalStorageDirectory();
File dir = new File(sdCard.getAbsolutePath() + "/" + Data.PIC_FOLDERNAME + "/" + Data.PIC_FILENAME);
if (dir.exists() == false) {
dir.mkdir();
}

String path = sdCard.getAbsolutePath() + "/" + Data.PIC_FOLDERNAME + "/" + Data.PIC_FILENAME + "/" + Data.CROP_FILENAME;
croppedFile = new File(path);

((CropActivity) getContext()).runOnUiThread(new Runnable() {
public void run() {
// things need to work on ui thread
Bitmap croppedBitmap = CropImageView.croppedBitmap;
FileOutputStream out;
try {
out = new FileOutputStream(croppedFile);
croppedBitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
} catch (Exception e) {
e.printStackTrace();
}
}
});


아래와 같이  UI Thread 이용하며 CropActivity는 위의 코드가 동작하는 View와 같은 Class가 호출되어 Activity 로 동작하는 Class 이다.

    ((CropActivity) getContext()).runOnUiThread(new Runnable() {
public void run() {
// things need to work on ui thread


}
});







Posted by injunech
2019.02.11 01:47


[Android] MyRemote PC컨트롤 리모컨


원격으로 PC를 조종할수 있는 앱입니다.
게임, 영화감상, 음악감상, PPT 발표 간단한 컴퓨터 조작,
스마트폰을 이용해 PC를 조종하세요.

자신이 원하는 키들을 조합한 리모컨화면을 생성 가능합니다.

스마트폰에 아래의 앱을 설치하세요

https://play.google.com/store/apps/details?id=com.cij.myremote_lite


PC에서 아래 경로의 실행파일을 받으세요.
http://myremote.kro.kr


사용방법은 다음 링크를 참고 하세요.
http://manual.myremote.kro.kr









'Project > Remote' 카테고리의 다른 글

MyRemote V1.81  (0) 2019.06.25
MyRemote Ver 1.8  (0) 2019.06.13
[Android] MyRemote PC컨트롤 리모컨  (0) 2019.02.11
MyRemote Ver 1.76  (0) 2017.04.10
MyRemote Ver 1.75  (0) 2017.03.12
MyRemote Ver 1.70  (0) 2015.06.04


Posted by injunech
2019.02.09 19:02


Google Android Sample Code Git

구글 안드로이드 샘플 코드 Git


Google Samples

https://github.com/googlesamples



Google Play In-app Billing Samples

https://github.com/googlesamples/android-play-billing






Posted by injunech
2019.01.28 02:31


Android 파일명 변경시


File beforeFileName;
File afterFileName;
 
beforeFileName = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "folder name", "target file name");
afterFileName = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "folder name", "modifi file name");
 
if (beforeFileName.renameTo(afterFileName))
    Toast.makeText(getApplicationContext(), "success!", Toast.LENGTH_SHORT).show();
else
    Toast.makeText(getApplicationContext(), "faile", Toast.LENGTH_SHORT).show();




Posted by injunech
2019.01.20 20:39


안드로이드 Task의 종료시점 확인하기

배경

안드로이드는 OS에 의해 프로세스가 강제종료 되는 시점을 알 수 없다. Application 클래스의 onTerminate()메서드조차 가상머신에서만 동작하고 실제 디바이스에서는 동작하지 않는다고 문서에 명시되어 있다.(실제로도 동작하지 않는 것을 확인했다.)

Task?

안드로이드 프로세스와 태스크의 차이

안드로이드에는 Task라는 개념이 있다. Task는 어떤 앱이 실행되면서, 관련 컴포넌트(엑티비티, 서비스, 리시버, 프로바이더)를 묶어놓은 그룹의 개념으로, 프로세스와는 약간 다르다. 그렇지만, 일반적인 앱이라면 하나의 앱을 실행할 때, 하나의 프로세스가 생기며, 그 프로세스에서 실행되는 모든 컴포넌트들은 하나의 Task로 묶이게 된다. 사용자가 확인할 수 있는 최근에 실행된 앱 보기에 나오는 하나의 단위가 바로 Task 이다.


Task 종료시점 알기

OS가 프로세스를 강제종료 시키는 시점은 알 수 없지만, Task가 종료되는 시점은 알 수 있다. 즉, 사용자가 최근에 실행된 앱 보기 화면에서 태스크를 지웠을 때의 시점을 감지할 수 있는 것이다. 한개의 프로세스와 한개의 Task로 구성되어 있는 앱이라면, 이 방법을 통해 사용자가 앱을 강제종료 시키는 시점 정도는 핸들링 할 수 있다.(이것은 프로세스가 종료되는 것과는 엄밀히 다르다. 그러므로, 프로세스의 종료시점과 혼동하여 사용하면 부작용을 초래할 수 있다.)
Service 클래스에는 Task가 종료되었을 때 콜백을 받는 onTaskRemoved()메서드가 존재한다. 이 메서드를 이용해, Task의 종료시점을 잡을 수 있다.


적용방법

1.Manifest에 서비스를 등록한다. 이 때, android:stopWithTask 속성을 반드시 false로 설정해야 한다. true로 설정하면 onTaskRemoved()메서드가 호출되지 않는다.
<application
    ...>
    ...
    <service android:name=".TestService"
                android:stopWithTask="false" />
    ...
</application>
2.Manifest에 선언한 Service를 선언하고, onTaskRemoved()메서드를 오버라이드하고 필요한 내용을 구현한다.
Note : super.onTaskRemoved()를 호출하게 되면, Task가 종료되는 시점에서 프로세스는 재시작된다.(Task는 안보이고 프로세스만 살아있는 형태) Task가 종료되는 시점에서 서비스도 같이 종료시키려면 stopSelf() 메서드를 호출해준다.
public class TestService extends Service {
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
    
    @Override
    public void onTaskRemoved(Intent rootIntent) {
        Logger.logWarn("onTaskRemoved - " + rootIntent);
        // 여기에 필요한 코드를 추가한다.,
        
        stopSelf();
    }
}
3.해당 Service를 앱의 시작점에서 시작한다.일반적으로 앱의 시작점은 Application의 onCreate()나 스플래쉬 엑티비티의 onCreate() 메서드일 것이다. Manifest에 등록된 리시버가 없다면, 두가지 방법은 차이가 없지만, 외부 액션을 받는 리시버가 등록되어 있다면, 그 외부액션을 받았을 때 Application의 onCreate()가 실행되기 때문에, 오동작할 우려가 있으므로, 그럴때는 가급적 엑티비티의 onCreate()에서 시작하도록 한다.
public class SplashActivity extends Activity {
    ...
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        startService(new Intent(this, TestService.class));
    }
    ...
}




Posted by injunech
2019.01.06 03:00


TextView 줄간격, 자간, 장평 설정하기


ㆍ 줄간격 : lineSpacingExtra, lineSpacingMultiplier

ㆍ 자간 : letterSpacing

ㆍ 장평 : textScaleX



줄 간격


android:lineSpacingExtra ="0dp"(기본)


android:lineSpacingExtra ="5dp"


android:lineSpacingMultiplier="1"(기본)


android:lineSpacingMultiplier="1.5"




자간


android:letterSpacing="0"(기본)


android:letterSpacing="0.2"


장평


android:textScaleX="1"(기본)


android:textScaleX="1.5"(기본)







Posted by injunech