태그 보관물: Crashlytics

안드로이드 페브릭(Fabric) 그래들(Gradle) 플러그인 버전 지정하기

페브릭(Fabric)은 아주 유용한 기능을 모바일 플랫폼에 쉽게 적용할 수 있게 도와주는 서비스이다. Crashlytics도 페브릭에서 제공하는 크래시 리포트 서비스이다. Crashlytics을 안드로이드에서 사용하는 방법은 fabric.io/kits/android/crashlytics/install 에서 확인할 수 있다. 여기에서 아래의 아래의 코드를 추가하라고 한다.

buildscript {
repositories {
maven { url 'https://maven.fabric.io/public' }
}

dependencies {
// These docs use an open ended version so that our plugin
// can be updated quickly in response to Android tooling updates

// We recommend changing it to the latest version from our changelog:
// https://docs.fabric.io/android/changelog.html#fabric-gradle-plugin
classpath 'io.fabric.tools:gradle:1.+'
}
}

위 코드를 보면 알겠지만, 페브릭의 그래들 플러그인 버전이 ‘io.fabric.tools:gradle:1.+’로 선언되어 있다. 주석을 읽어보면 알겠지만, docs.fabric.io/android/changelog.html#fabric-gradle-plugin 에서 최신 버전으로 수정하라고 한다. 이 문서를 보면, 아래와 같이 최신 버전을 확인할 수 있고, 변경 내용도 확인할 수 있다.

2017년 6월 14일에 확인해 본 결과로 1.22.2 버전이 최신버전이다.

위 과정에 더불어, 페브릭 그래들 플러그인에 대한 메타 정보는 아래 URL에서 확인할 수 있다.
fabric-artifacts.s3.amazonaws.com/public/io/fabric/tools/gradle/maven-metadata.xml

위 이미지에서 최신 버전 릴리즈 시점이 2017년 05월 31일 이라는 것도 알 수 있다.

결론으로 app 모듈에서 build.gradle 파일의 맨 처음은 아래와 같은 모습이 될 것이다.

buildscript {
    repositories {
        jcenter()
        maven { url 'https://maven.fabric.io/public' }
    }

    dependencies {
        classpath 'com.android.tools.build:gradle:2.3.3'

        classpath 'io.fabric.tools:gradle:1.22.2'
    }
}

안드로이드 스튜디오(Android Studio)에서 Fabric의 Crashlytics을 플러그인 설치 없이 사용하기

Crashlytics는 안드로이드나 IOS에서 크래시나 그와 같은 상황에 대한 리포팅 서비스를 제공하고 있다. 이 서비스를 사용하는 기본 방법은 http://sjava.net/?p=479를 확인하면 쉽게 알 수 있다. 필자가 진행하고 있는 프로젝트로 외부 프로젝트도 같이 진행하고 있다. 그래서 Crashlytics 계정을 2개 이상 사용하고 있다.

프로젝트를 변경해서 다른 계정을 사용할 때는 Crashlytics의 플러그인에서 로그아웃 -> 로그인 해서 사용하는 계정을 변경해서 사용한다. 그리고 이 플러그인을 설치하고 싶지가 않았다. 그래서 이 글에서는 플러그인을 설치하지 않고 Crashlytics를 사용하는 방법을 살펴보자.

Crashlytics는 트위터(Twitter)가 인수를 하고 Fabric이라는 통합 툴의 일부가 되었다. 그리고 사용하는 방법도 변경이 되었기에 변경된 내용을 살펴보자.

1. Fabric에서 Crashlytics에 해당하는 gradle 설정은 다음과 같다. 아래 내용은 https://fabric.io/migrations/gradle 에서 확인 할 수 있다.

buildscript {
  repositories {
    jcenter()
    maven { url 'https://maven.fabric.io/public' }
  }
  dependencies {
    classpath 'com.android.tools.build:gradle:1.5.0'
    // The Fabric Gradle plugin uses an open ended version to react
    // quickly to Android tooling updates
    classpath 'io.fabric.tools:gradle:1.+'
  }
}

apply plugin: 'com.android.application'
apply plugin: 'io.fabric'

repositories {
  jcenter()
    maven { url 'https://maven.fabric.io/public' }
  }

android {
  compileSdkVersion 23
  buildToolsVersion "23.0.0"

  defaultConfig {
    minSdkVersion 16
    targetSdkVersion 23
  }
}

dependencies {
  // Crashlytics Kit
  compile('com.crashlytics.sdk.android:crashlytics:2.5.5@aar') {
    transitive = true
  }
}

2. AndroidManifest.xml 파일에 아래의 권한을 추가한다.

<manifest>
    <uses-permission android:name="android.permission.INTERNET" />
</manifest>

3. AndroidManifest.xml 의 <application> 요소에 아래의 값을 추가한다.

<application>
    <meta-data android:name="com.crashlytics.ApiKey" android:value="키를 넣어 주세요" />
</application>

3.1 위에서 필요한 키는 웹사이트에서 Settings > Organizations 의 목록에 있는 Admin 그룹을 클릭하면 사용자 목록을 볼 수 있고, 이 화면에서 API 키를 확인할 수 있다.

4. Application 클래스에서 사용
– Application 클래스를 상속하는 클래스의 onCreate() 메서드에서 아래와 같은 코드를 사용해서 Crashlytics를 사용할 수 있다.

public void onCreate() {
    super.onCreate();
    Fabric.with(this, new Crashlytics());
}

이제 아래 과정은 Crashlytics가 Fabric으로 통합되면서 Deprecated된 과정이다. 따라서, 아래의 과정은 참고를 하지 않는게 좋다.

이 과정은 안드로이드스튜디오(Android Studio)에 적용할 수 있다.

1. Crashlytics의 pom.xml 파일을 확인한다.
https://crashlytics.com/downloads/maven 에서 Crashlytics에서 배포하는 바이너리에 대한 정보를 확인할 수 있다.

2. 위 정보를 기준으로 build.gradle에 아래의 정보를 입력한다.

buildscript {
    repositories {
        // 1. Add the Crashlytics Maven repository
        maven { url 'http://download.crashlytics.com/maven' }
    }
    dependencies {
        // 2. Add the Crashlytics plugin to your dependencies
        classpath 'com.crashlytics.tools.gradle:crashlytics-gradle:1.+'
    }
}

apply plugin: 'com.android.application'

// 3. Apply the Crashlytics plugin after the Android plugin
apply plugin: 'crashlytics'

repositories {
    // 4. Add the Maven repository
    maven { url 'http://download.crashlytics.com/maven' }
}

dependencies {
    // 5. Add the Crashlytics SDK to the Android project
    compile 'com.crashlytics.android:crashlytics:1.+'
}

3. AndroidManifest.xml 파일에 아래의 권한을 추가한다.

<manifest>
    <uses-permission android:name="android.permission.INTERNET" />
</manifest>

4. AndroidManifest.xml 의 <application> 요소에 아래의 값을 추가한다.

<application>
    <meta-data android:name="com.crashlytics.ApiKey" android:value="키를 넣어 주세요" />
</application>

4.1 위에서 필요한 키는 웹사이트에서 Settings > Organizations 의 목록에 있는 Admin 그룹을 클릭하면 사용자 목록을 볼 수 있고, 이 화면에서 API 키를 확인할 수 있다.

5. Application 클래스에서 사용
– Application 클래스를 상속하는 클래스의 onCreate() 메서드에서 아래와 같이 Crashlytics를 사용할 수 있다.

public void onCreate() {
    super.onCreate();
    Crashlytics.start(this);
}

 

* 레퍼런스
http://blog.danlew.net/2015/02/20/setting-up-a-new-app-in-crashlytics-without-the-plugin/

안드로이드 애플리케이션에서 스레드의 UncaughtExceptionHandler 인터페이스를 사용해보자

안드로이드 애플리케이션을 사용하다 보면 애플리케이션이 비정상적으로 종료하는 경우를 볼 수 있다. 이런 경우에 http://www.crashlytics.com과 같은 서비스에 가입해서 크래시에 대한 정보를 확인할 수도 있다. 필자도 이 서비스를 사용해서 크래시 리포팅을 받고 이 정보를 기준으로 버그를 수정하고 다시 배포한다.

여기에서 크래시에 대한 정보는 어떻게 수집을 할까?에 대한 간단한 답을 해보고 한다. “자바로 개발하는 애플리케이션은 모두 스레드다”라고 할 정도로 자바는 스레드 위에서 동작하게 된다. 물론 안드로이드도 스레드 기반에서 동작한다. 스레드 클래스(Thread)를 보면 UncaughtException을 처리할 수 있는 핸들러를 등록할 수 있도록 인터페이스를 제공한다. 10년 자바를 개발했는데 이제서야 봤다. ㅠ.ㅠ

이 인터페이스를 사용해서 안드로이드 애플리케이션이 UncaughtException을 캐치하는 형태를 살펴보자. 안드로이드 애플리케이션에서 예외를 캐치하지 않아서 크래시가 발생하면 화면에 다음과 같은 다이얼로그가 뜨게 된다.

그럼 안드로이드 애플리케이션에서 위 화면과 같은 다이얼로그가 어떻게 뜨게 되는지 살펴보자.
안드로이드에서 애플리케이션에서 캐치하지 않은 예외가 발생하면 처리하는 기본 ExceptionHandler는 다음과 같다.

이 화면은 디버거로 안드로이드 기본 UncaughExceptionHandler의 구현체를 확인한 화면이다. 이 구현체는 com.android.internal.os.RuntimInit 클래스에 있다는 것을 알 수 있다.

위에서 확인한 RuntimInit 클래스의 기본 UncaughtExceptionHandler의 구현체는 다음과 같다.

    /**
     * Use this to log a message when a thread exits due to an uncaught
     * exception.  The framework catches these for the main threads, so
     * this should only matter for threads created by applications.
     */
    private static class UncaughtHandler implements Thread.UncaughtExceptionHandler {
        public void uncaughtException(Thread t, Throwable e) {
            try {
                // Don't re-enter -- avoid infinite loops if crash-reporting crashes.
                if (mCrashing) return;
                mCrashing = true;

                if (mApplicationObject == null) {
                    Clog_e(TAG, "*** FATAL EXCEPTION IN SYSTEM PROCESS: " + t.getName(), e);
                } else {
                    StringBuilder message = new StringBuilder();
                    message.append("FATAL EXCEPTION: ").append(t.getName()).append("\n");
                    final String processName = ActivityThread.currentProcessName();
                    if (processName != null) {
                        message.append("Process: ").append(processName).append(", ");
                    }
                    message.append("PID: ").append(Process.myPid());
                    Clog_e(TAG, message.toString(), e);
                }

                // Bring up crash dialog, wait for it to be dismissed
                ActivityManagerNative.getDefault().handleApplicationCrash(
                        mApplicationObject, new ApplicationErrorReport.CrashInfo(e));
            } catch (Throwable t2) {
                try {
                    Clog_e(TAG, "Error reporting crash", t2);
                } catch (Throwable t3) {
                    // Even Clog_e() fails!  Oh well.
                }
            } finally {
                // Try everything to make sure this process goes away.
                Process.killProcess(Process.myPid());
                System.exit(10);
            }
        }
    }

위 UncaughtExceptionHandler가 캐치하지 않은 예외상황을 처리하는 단계는 다음과 같다.

  1. 프로세스가 크래시 상태라는 것을 알려준다.
  2. 메세지에 스레드 이름과 프로세스 이름등을 로그로 전달한다.
  3. ActivityManagerNative를 구현하고 있는 클래스(결국 ActivityManager라고 보면 된다)에게 애플리케이션에서 크래시가 발생했으니 다이얼 로그를 보여주고 입력을 받도록 요청한다.
  4. finally 부분에서 애플리케이션의 프로세스를 종료시킨다.

이상 안드로이드가 기본으로 UncaughtExceptionHandler를 살펴봤다. 다음으로 UncaughtExceptionHandler를 사용해서 crashlytics과 같은 처리를 하기 위한 과정을 살펴보자. 여기에서는 Application과 2개의 Activity를 사용한다.

* MainApplication 클래스
– 이 클래스에서 UncaughtExceptionHandler을 구현한다. 이 UncaughtExceptionHandler 클래스에서는 크래시에 대한 다이얼로그를 보여주지 않고 바로 애플리케이션을 종료시킨다.

package net.sjava.ex.threaduncatchexception;

import android.app.Application;
import android.util.Log;

public class MainApplication extends Application {
	static String CNAME = MainApplication.class.getSimpleName();
	
	private Thread.UncaughtExceptionHandler androidDefaultUEH;
	private UncaughtExceptionHandler unCatchExceptionHandler;
	
	public UncaughtExceptionHandler getUncaughtExceptionHandler() {
		return unCatchExceptionHandler;
	}
	
	@Override
	public void onCreate() {
		androidDefaultUEH = Thread.getDefaultUncaughtExceptionHandler();
		unCatchExceptionHandler = new UncaughtExceptionHandler();
		
		Thread.setDefaultUncaughtExceptionHandler(unCatchExceptionHandler);
	}
	
	public class UncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
		@Override
		public void uncaughtException(Thread thread, Throwable ex) {
			// 이곳에서 로그를 남기는 작업을 하면 된다.
			Log.e(CNAME, "error -----------------> ");
			
			// 
			android.os.Process.killProcess(android.os.Process.myPid());
            System.exit(10);
            
			//androidDefaultUEH.uncaughtException(thread, ex);
		}
	}
}

* BaseActivity 클래스
– 이 클래스는 애플리케이션에서 사용하는 Activity 클래스의 부모 클래스이다. 여기에서 스레드의 기본 UncatchExceptionHandler로 MainApplication에서 구현한 UncaughtExceptionHandler를 사용하도록 설정한다.

package net.sjava.ex.threaduncatchexception;

import android.app.Activity;
import android.os.Bundle;

public class BaseActivity extends Activity {
	protected String CNAME = MainActivity.class.getSimpleName();
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		CNAME = this.getClass().getSimpleName();
		
		Thread.setDefaultUncaughtExceptionHandler(((MainApplication)getApplication()).getUncaughtExceptionHandler());
	}
}

* MainActivity 클래스
– 이 Activity 클래스가 애플리케이션의 시작 클래스이다. 그리고 위의 BaseActivity를 상속하고 있다. 테스트를 위해서 2개의 버튼을 사용하고 있다. 첫 번째 버튼은 UI 스레드에서 발생하는 예외 상황을 태스트할 수 있고, 두 번째 버튼은 AsyncTask를 사용해서 일반 스레드에서 발생하는 에러를 태스트 할 수 있다.

package net.sjava.ex.threaduncatchexception;

import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.View;

public class MainActivity extends BaseActivity {
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		super.setContentView(R.layout.activity_main);
	}

	public void OnClick(View v) {
		if(v.getId() == R.id.button1) {
			
			String temp = "aaaaaaaaaaaa";
			String[] tempArray = temp.split(" ");
			
			Log.d(CNAME, tempArray[1]);
		}
		
		if(v.getId() == R.id.button2) {
			new TestAsyncTask().execute("");
		}
	}
	
	public class TestAsyncTask extends AsyncTask<String, Integer, String> {
		@Override
		protected String doInBackground(String... params) {
			String temp = "aaaaaaaaaaaa";
			String[] tempArray = temp.split(" ");
			
			return tempArray[1].toLowerCase();
		}
	}

}

이상 Thread 클래스가 선언하고 있는 UncaughtExceptionHandler를 사용해서 캐치되지 않은 예외 상황을 애플리케이션에서 처리할 수 있는 기회를 가질 수 있는 것을 확인해 봤다. UncaughtExceptionHandler 인터페이스를 이용해서 간단한 crashlytics과 같은 서비스도 제공할 수 있을 것으로 기대한다.

위에서 살펴본 예제는 다운로드할 수 있다.

Crashlytics로 안드로이드 앱 크래시 확인하기..

클라이언트를 개발하고 유지하다 보면 늘 부딪히는 문제가 크래시에 대한 리포팅을 받기 힘들다는 것이다. 이런 문제를 해결하기 위해서 크래시 덤프를 서버로 전송하는 히든 프로세스를 띄우기도 한다. 안드로이드 앱의 경우 Play 마켓을 사용하고 있다면, 크래시와 ANR(Application Not Responding)에 대한 리포팅을 유저의 동의하에 받을 수 있다. 하지만 이 방법도 유저가 리포팅을 하지 않는다면 없는 것이나 마찬가지이다.

2012년 Google IO의 최대 화두가 안드로이드 앱을 위한 통계와 리포트등을 지원하는 서비스들이라는 것은 위 상황을 잘 해결해 주는 서비스가 많이 생겨났다는 것이고, 여기서는 트위터가 인수한 Crashlytics를 사용해서 안드로이드 크래시에 대한 리포팅을 받아보도록 하겠다.

1. Crashlytics(http://www.crashlytics.com/) 가입
– 이 사이트에 들어가서, 이메일 주소를 입력하고 Try Crashlytics를 클릭하면 등록한 이메일 주소로 이메일이 오고, 이메일을 통해서 validation(시간이 좀 걸림)을 해야 함.

2. 플러그인 설정
https://www.crashlytics.com/downloads/plugins 에서 사용하는 IDE에 맞는 플러그인을 선택해서 설치한다.

2.1 이클립스를 기준으로 설명을 하면..
– Help > install New Software 에서 https://crashlytics.com/download/eclipse를 입력해서 플러그인을 설치한다.

3. Crashlytics 실행
3.1 아래 이미지의 빨간색 아이콘을 클릭하면, Crashlytics가 실행이 되고, ID/PW를 입력하면 Crashlytics 앱이 실행된다.

 3.2 여기에서 앱을 선택하고, Next를 눌러서 리포팅을 위한 코드를 삽입한다.

추가되는 데이터는
– Application 태그안에 메타 데이터로 Crashlytics 메타 데이터(ID의 API Key)를 추가한다.
– Internet 퍼미션이 없는 경우에는 이 퍼미션도 같이 추가가 된다.

이제 준비가 다 됐고, 실행해 보자.

4. 테스트
간단하게 아래의 코드를 입력해서 크래시 상황을 만들어 보자

throw new RuntimeException ("This is a crash");

위 코드를 추가한 앱을 실행하면 Crashlytics 앱의 waiting 상태가 아래와 같이 바뀌게 된다.

이제 Crashlytics.com에서 로그인을 하면 리포팅된 데이터를 확인할 수 있다. 그리고, Crashlytics에 대한 기술적인 이슈는 http://support.crashlytics.com/knowledgebase에서 해결할 수 있다.

Crashlytics을 iOS에서 사용하는 방법은 http://lab.ash84.net/1048에서 확인하세요.