Đây là giải pháp hoàn chỉnh (hầu như: Tôi đã bỏ qua bố cục giao diện người dùng và xử lý nút) - có được từ rất nhiều thử nghiệm và nhiều bài đăng khác nhau của những người khác liên quan đến các vấn đề nảy sinh trong quá trình này.
Có một số điều bạn cần làm:
- Xử lý ngoại lệ uncaughtException trong lớp con Ứng dụng của bạn.
- Sau khi bắt được một ngoại lệ, hãy bắt đầu một hoạt động mới để yêu cầu người dùng gửi nhật ký.
- Trích xuất thông tin nhật ký từ các tệp của logcat và ghi vào tệp của riêng bạn.
- Khởi động ứng dụng email, cung cấp tệp của bạn dưới dạng tệp đính kèm.
- Tệp kê khai: lọc hoạt động của bạn để trình xử lý ngoại lệ của bạn nhận ra.
- Theo tùy chọn, thiết lập Proguard để loại bỏ Log.d () và Log.v ().
Bây giờ, đây là chi tiết:
(1 & 2) Xử lý ngoại lệ, bắt đầu hoạt động nhật ký gửi:
public class MyApplication extends Application
{
public void onCreate ()
{
// Setup handler for uncaught exceptions.
Thread.setDefaultUncaughtExceptionHandler (new Thread.UncaughtExceptionHandler()
{
@Override
public void uncaughtException (Thread thread, Throwable e)
{
handleUncaughtException (thread, e);
}
});
}
public void handleUncaughtException (Thread thread, Throwable e)
{
e.printStackTrace(); // not all Android versions will print the stack trace automatically
Intent intent = new Intent ();
intent.setAction ("com.mydomain.SEND_LOG"); // see step 5.
intent.setFlags (Intent.FLAG_ACTIVITY_NEW_TASK); // required when starting from Application
startActivity (intent);
System.exit(1); // kill off the crashed app
}
}
(3) Trích xuất nhật ký (Tôi đặt đây là Hoạt động SendLog của tôi):
private String extractLogToFile()
{
PackageManager manager = this.getPackageManager();
PackageInfo info = null;
try {
info = manager.getPackageInfo (this.getPackageName(), 0);
} catch (NameNotFoundException e2) {
}
String model = Build.MODEL;
if (!model.startsWith(Build.MANUFACTURER))
model = Build.MANUFACTURER + " " + model;
// Make file name - file must be saved to external storage or it wont be readable by
// the email app.
String path = Environment.getExternalStorageDirectory() + "/" + "MyApp/";
String fullName = path + <some name>;
// Extract to file.
File file = new File (fullName);
InputStreamReader reader = null;
FileWriter writer = null;
try
{
// For Android 4.0 and earlier, you will get all app's log output, so filter it to
// mostly limit it to your app's output. In later versions, the filtering isn't needed.
String cmd = (Build.VERSION.SDK_INT <= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) ?
"logcat -d -v time MyApp:v dalvikvm:v System.err:v *:s" :
"logcat -d -v time";
// get input stream
Process process = Runtime.getRuntime().exec(cmd);
reader = new InputStreamReader (process.getInputStream());
// write output stream
writer = new FileWriter (file);
writer.write ("Android version: " + Build.VERSION.SDK_INT + "\n");
writer.write ("Device: " + model + "\n");
writer.write ("App version: " + (info == null ? "(null)" : info.versionCode) + "\n");
char[] buffer = new char[10000];
do
{
int n = reader.read (buffer, 0, buffer.length);
if (n == -1)
break;
writer.write (buffer, 0, n);
} while (true);
reader.close();
writer.close();
}
catch (IOException e)
{
if (writer != null)
try {
writer.close();
} catch (IOException e1) {
}
if (reader != null)
try {
reader.close();
} catch (IOException e1) {
}
// You might want to write a failure message to the log here.
return null;
}
return fullName;
}
(4) Bắt đầu một ứng dụng email (cũng trong Hoạt động SendLog của tôi):
private void sendLogFile ()
{
String fullName = extractLogToFile();
if (fullName == null)
return;
Intent intent = new Intent (Intent.ACTION_SEND);
intent.setType ("plain/text");
intent.putExtra (Intent.EXTRA_EMAIL, new String[] {"log@mydomain.com"});
intent.putExtra (Intent.EXTRA_SUBJECT, "MyApp log file");
intent.putExtra (Intent.EXTRA_STREAM, Uri.parse ("file://" + fullName));
intent.putExtra (Intent.EXTRA_TEXT, "Log file attached."); // do this so some email clients don't complain about empty body.
startActivity (intent);
}
(3 & 4) Đây là giao diện của SendLog (mặc dù vậy, bạn sẽ phải thêm giao diện người dùng):
public class SendLog extends Activity implements OnClickListener
{
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
requestWindowFeature (Window.FEATURE_NO_TITLE); // make a dialog without a titlebar
setFinishOnTouchOutside (false); // prevent users from dismissing the dialog by tapping outside
setContentView (R.layout.send_log);
}
@Override
public void onClick (View v)
{
// respond to button clicks in your UI
}
private void sendLogFile ()
{
// method as shown above
}
private String extractLogToFile()
{
// method as shown above
}
}
(5) Bản kê khai:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" ... >
<!-- needed for Android 4.0.x and eariler -->
<uses-permission android:name="android.permission.READ_LOGS" />
<application ... >
<activity
android:name="com.mydomain.SendLog"
android:theme="@android:style/Theme.Dialog"
android:textAppearance="@android:style/TextAppearance.Large"
android:windowSoftInputMode="stateHidden">
<intent-filter>
<action android:name="com.mydomain.SEND_LOG" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
</application>
</manifest>
(6) Thiết lập Proguard:
Trong project.properties, hãy thay đổi dòng cấu hình. Bạn phải chỉ định "tối ưu hóa" nếu không Proguard sẽ không loại bỏ các lệnh gọi Log.v () và Log.d ().
proguard.config=${sdk.dir}/tools/proguard/proguard-android-optimize.txt:proguard-project.txt
Trong proguard-project.txt, hãy thêm phần sau. Điều này yêu cầu Proguard giả sử Log.v và Log.d không có tác dụng phụ (mặc dù chúng có tác dụng phụ vì chúng ghi vào nhật ký) và do đó có thể bị xóa trong quá trình tối ưu hóa:
-assumenosideeffects class android.util.Log {
public static int v(...);
public static int d(...);
}
Đó là nó! Nếu bạn có bất kỳ đề xuất nào để cải thiện điều này, vui lòng cho tôi biết và tôi có thể cập nhật điều này.