Service超时导致应用ANR问题记录
需求:需要机器在第一次开机后,后台执行Service拷贝文件到机器的另一个目录
Bug描述:发现拷贝不全,有一部分文件没有拷贝,
抓取日志分析:
10-15 08:27:49.391 1669 1669 D TXZ : addModule[com.txznet.txz.module.v.b@cd224a6] cost time: 4ms
10-15 08:27:49.397 540 569 W ActivityManager: Timeout executing service: ServiceRecord{fc7671d u0 com.example.tmedia/.XXXXService}
10-15 08:27:49.425 388 388 D btopt : ----------SendCurrentState Control_fd = 0----------
发现Service超时了,导致ANR这个进程被系统kill掉了,可能是copy的文件太大了,需要的时间超过了20s
Android 诱发应用ANR的原因,Service忙导致超时20s无响应。
InputDispatching Timeout:5秒内无法响应屏幕触摸事件或键盘输入事件
BroadcastQueue Timeout :在执行前台广播(BroadcastReceiver)的onReceive()函数时10秒没有处理完成,后台为60秒。
Service Timeout :前台服务20秒内,后台服务在200秒内没有执行完毕。
ContentProvider Timeout :ContentProvider的publish在10s内没进行完。
不建议讲耗时操作(获取数据,copy等)放在Service内进行。
如何解决:
官方实例
ANR记录
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.tmedia">
<!--前台服务权限-->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<!-- sd读写权限-->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!--wake-lock-->
<uses-permission android:name="android.permission.WAKE_LOCK"></uses-permission>
<application
android:allowBackup="true"
android:excludeFromRecents="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:requestLegacyExternalStorage="true"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<!-- <category android:name="android.intent.category.LAUNCHER" />-->
</intent-filter>
</activity>
<service
android:name=".JobCopyService"
android:enabled="true"
android:exported="true"
android:permission="android.permission.BIND_JOB_SERVICE"></service>
<service
android:name=".CopyService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.example.tmedia.copyservice.start" />
</intent-filter>
</service>
</application>
</manifest>
CopyService.java
public class CopyService extends Service {
private static final String TAG = "CopyService";
private static CopyCanbusService instance;
private Context mContext;
public static CopyCanbusService getInstance() {
return instance;
}
@Override
public void onCreate() {
super.onCreate();
//startForeground(); // -->new
Log.d(TAG, "onCreate");
}
private void startForeground() {
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
NotificationChannel mChannel = null;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
String CHANNEL_ID_STRING = "com.wwt.wwtSetting";
mChannel = new NotificationChannel(CHANNEL_ID_STRING, "wwt", NotificationManager.IMPORTANCE_NONE);
notificationManager.createNotificationChannel(mChannel);
Notification notification = new Notification.Builder(getApplicationContext(), CHANNEL_ID_STRING).build();
startForeground(1, notification);
}
}
@SuppressLint("WrongConstant")
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand");
flags = START_STICKY;
startForeground(); //-->old
Intent copyCanbusIntent = new Intent();
JobCopyService.enqueueWork(getApplicationContext(), copyCanbusIntent);
stopForeground(true);
return super.onStartCommand(intent, flags, startId);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy");
}
}
JobCopyService.java
public class JobCopyService extends JobIntentService {
private static final String TAG = "JobCopyService";
private static JobCopyCanbusService instance;
private Context mContext;
/**
* Unique job ID for this service.
*/
private static final int JOB_ID = 1000;
static void enqueueWork(Context context, Intent work) {
enqueueWork(context, JobCopyCanbusService.class, JOB_ID, work);
}
public static JobCopyCanbusService getInstance() {
return instance;
}
@Override
protected void onHandleWork(@NonNull Intent intent) {
Log.d(TAG, "onHandleWork-->" + intent);
//:/system 1.0
String oldDir = Environment.getRootDirectory().toString() + "/media/wwt";
String oldDirNew = Environment.getRootDirectory().toString() + "/media/WWT";
// :/vendor 1.1
/*String oldDir = "/vendor/media/wwt";
String oldDirNew = "/vendor/media/WWT";*/
String newDir = Environment.getExternalStorageDirectory().getAbsolutePath();// /storage/emulated/0
Log.d(TAG, "newDir:" + newDir);
File oldFiles = new File(oldDir);//原始文件地址
File oldFilesNew = new File(oldDirNew);//原始文件地址
String oldFileName = oldFiles.getName();//文件名字
String oldFileNameNew = oldFilesNew.getName();//文件新名字
if (!oldFiles.exists() && !oldFilesNew.exists()) {
Log.d(TAG, "Files wwt and WWT exists is no");
return;
}
if (oldFilesNew.exists()) {
Log.d(TAG, "oldFilesNew:" + oldFilesNew);
File filePublic = new File(newDir, oldFileNameNew);
if (!filePublic.exists()) {
filePublic.mkdirs();
}
copyFolder(oldFilesNew, filePublic);
} else if (oldFiles.exists()) {
Log.d(TAG, "oldFiles:" + oldFiles);
File filePublic = new File(newDir, oldFileName);
if (!filePublic.exists()) {
filePublic.mkdirs();
}
copyFolder(oldFiles, filePublic);
}
}
private static void copyFolder(File newFiles, File oldFile) {
try {
File[] files = newFiles.listFiles();
for (File srcFile : files) {
//如果是文件夹
if (srcFile.isDirectory()) {
//复制过来
//Log.d(TAG, "srcFile:" + srcFile);
String fileName = srcFile.getName();
File newFolder = new File(oldFile, fileName);
if (!newFolder.exists()) {
newFolder.mkdir();
copyFolder(srcFile, newFolder);
}
} else {
//如果是文件
String srcFileName = srcFile.getName();
File newFile = new File(oldFile, srcFileName);
//Log.d(TAG, "newFile:" + newFile);
//字
FileInputStream fis = new FileInputStream(srcFile);
FileOutputStream fos = new FileOutputStream(newFile, false);//构建文件输出流的对象,即将文件复制在哪里去,后面的true代表每次写入时不清空当前文件内容
byte[] bytes = new byte[1024];
int ten;
while ((ten = fis.read(bytes)) != -1) {
fos.write(bytes, 0, ten);//1024
fos.flush();
}
fis.close();
fos.close();
//Log.d(TAG, "close");
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "on onDestroy --> all work complete");
}
}