diff --git a/app_ipc_monitoring/src/main/AndroidManifest.xml b/app_ipc_monitoring/src/main/AndroidManifest.xml index 68c61efc7b..eff7016ee7 100644 --- a/app_ipc_monitoring/src/main/AndroidManifest.xml +++ b/app_ipc_monitoring/src/main/AndroidManifest.xml @@ -32,6 +32,7 @@ queue; private final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS ", Locale.getDefault()); private Future future; - // private final List listeners = new ArrayList<>(); - private OnAdasClientListener listener; + private Pair listener; private volatile boolean isCutDown = true;//是否缩短日志显示 private DataDistribution() { @@ -58,17 +60,16 @@ public class DataDistribution { return INSTANCE; } - public void registerOnAdasClientListener(OnAdasClientListener listener) { - this.listener = null; + public void registerAdasClientListener(Pair listener) { this.listener = listener; -// if (!listeners.contains(listener)) { -// listeners.add(listener); -// } } -// public void unregisterOnAdasClientListener(OnAdasClientListener listener) { -// listeners.remove(listener); -// } + public void unregisterAdasClientListener(Pair listener) { + if (this.listener != null && listener != null) + if (listener.second.hashCode() == this.listener.second.hashCode()) { + this.listener = null; + } + } public void addData(BaseInfo info) { @@ -103,7 +104,6 @@ public class DataDistribution { public final List listMogoReportMessage = new ArrayList<>(); public final List listPerceptionTrafficLight = new ArrayList<>(); public final List listPredictionObstacleTrajectory = new ArrayList<>(); - public final List listBasicInfoReq = new ArrayList<>(); public final List listRecordPanel = new ArrayList<>(); public final List listGlobalPathResp = new ArrayList<>(); public final List listWarn = new ArrayList<>(); @@ -127,220 +127,128 @@ public class DataDistribution { if (listTrajectory.size() > LIST_SIZE) { listTrajectory.remove(listTrajectory.size() - 1); } - if (listener != null) { - listener.onRefresh(); + if (listener != null && Constants.TITLE.RECEIVE_TRAJECTORY.equals(listener.first)) { + listener.second.onRefresh(); } -// if (!listeners.isEmpty()) { -// for (OnAdasClientListener listener : listeners) { -// listener.onRefresh(); -// } -// } } else if (data instanceof TrackedObjects) { listTrackedObjects.add(0, time + str); if (listTrackedObjects.size() > LIST_SIZE) { listTrackedObjects.remove(listTrackedObjects.size() - 1); } - if (listener != null) { - listener.onRefresh(); + if (listener != null && Constants.TITLE.RECEIVE_TRACKED_OBJECTS.equals(listener.first)) { + listener.second.onRefresh(); } -// if (!listeners.isEmpty()) { -// for (OnAdasClientListener listener : listeners) { -// listener.onRefresh(); -// } -// } } else if (data instanceof MyPointCloud) { listPointCloud.add(0, time + str); if (listPointCloud.size() > LIST_SIZE) { listPointCloud.remove(listPointCloud.size() - 1); } - if (listener != null) { - listener.onRefresh(); + if (listener != null && Constants.TITLE.RECEIVE_POINT_CLOUD.equals(listener.first)) { + listener.second.onRefresh(); } -// if (!listeners.isEmpty()) { -// for (OnAdasClientListener listener : listeners) { -// listener.onRefresh(); -// } -// } } else if (data instanceof PlanningObjects) { listPlanningObjects.add(0, time + str); if (listPlanningObjects.size() > LIST_SIZE) { listPlanningObjects.remove(listPlanningObjects.size() - 1); } - if (listener != null) { - listener.onRefresh(); + if (listener != null && Constants.TITLE.RECEIVE_PLANNING_OBJECTS.equals(listener.first)) { + listener.second.onRefresh(); } -// if (!listeners.isEmpty()) { -// for (OnAdasClientListener listener : listeners) { -// listener.onRefresh(); -// } -// } } else if (data instanceof GnssInfo) { listGnssInfo.add(0, time + str); if (listGnssInfo.size() > LIST_SIZE) { listGnssInfo.remove(listGnssInfo.size() - 1); } - if (listener != null) { - listener.onRefresh(); + if (listener != null && Constants.TITLE.RECEIVE_GNSS_INFO.equals(listener.first)) { + listener.second.onRefresh(); } -// if (!listeners.isEmpty()) { -// for (OnAdasClientListener listener : listeners) { -// listener.onRefresh(); -// } -// } } else if (data instanceof VehicleState) { listVehicleState.add(0, time + str); if (listVehicleState.size() > LIST_SIZE) { listVehicleState.remove(listVehicleState.size() - 1); } - if (listener != null) { - listener.onRefresh(); + if (listener != null && Constants.TITLE.RECEIVE_VEHICLE_STATE.equals(listener.first)) { + listener.second.onRefresh(); } -// if (!listeners.isEmpty()) { -// for (OnAdasClientListener listener : listeners) { -// listener.onRefresh(); -// } -// } } else if (data instanceof AutopilotState) { listAutopilotState.add(0, time + str); if (listAutopilotState.size() > LIST_SIZE) { listAutopilotState.remove(listAutopilotState.size() - 1); } - if (listener != null) { - listener.onRefresh(); + if (listener != null && Constants.TITLE.RECEIVE_AUTOPILOT_STATE.equals(listener.first)) { + listener.second.onRefresh(); } -// if (!listeners.isEmpty()) { -// for (OnAdasClientListener listener : listeners) { -// listener.onRefresh(); -// } -// } } else if (data instanceof MogoReportMessage) { listMogoReportMessage.add(0, time + str); if (listMogoReportMessage.size() > LIST_SIZE) { listMogoReportMessage.remove(listMogoReportMessage.size() - 1); } - if (listener != null) { - listener.onRefresh(); + if (listener != null && Constants.TITLE.RECEIVE_REPORT_MESSAGE.equals(listener.first)) { + listener.second.onRefresh(); } -// if (!listeners.isEmpty()) { -// for (OnAdasClientListener listener : listeners) { -// listener.onRefresh(); -// } -// } } else if (data instanceof PerceptionTrafficLight) { listPerceptionTrafficLight.add(0, time + str); if (listPerceptionTrafficLight.size() > LIST_SIZE) { listPerceptionTrafficLight.remove(listPerceptionTrafficLight.size() - 1); } - if (listener != null) { - listener.onPerceptionTrafficLight((PerceptionTrafficLight) data); + if (listener != null && Constants.TITLE.RECEIVE_PERCEPTION_TRAFFIC_LIGHT.equals(listener.first)) { + listener.second.onPerceptionTrafficLight((PerceptionTrafficLight) data); } -// if (!listeners.isEmpty()) { -// for (OnAdasClientListener listener : listeners) { -// listener.onPerceptionTrafficLight((PerceptionTrafficLight) data); -// } -// } } else if (data instanceof PredictionObstacleTrajectory) { listPredictionObstacleTrajectory.add(0, time + str); if (listPredictionObstacleTrajectory.size() > LIST_SIZE) { listPredictionObstacleTrajectory.remove(listPredictionObstacleTrajectory.size() - 1); } - if (listener != null) { - listener.onRefresh(); + if (listener != null && Constants.TITLE.RECEIVE_PREDICTION_OBSTACLE_TRAJECTORY.equals(listener.first)) { + listener.second.onRefresh(); } -// if (!listeners.isEmpty()) { -// for (OnAdasClientListener listener : listeners) { -// listener.onRefresh(); -// } -// } } else if (data instanceof BasicInfoReq) { - listBasicInfoReq.add(0, time + str); - if (listBasicInfoReq.size() > LIST_SIZE) { - listBasicInfoReq.remove(listBasicInfoReq.size() - 1); - } - if (listener != null) { - listener.onRefresh(); - } -// if (!listeners.isEmpty()) { -// for (OnAdasClientListener listener : listeners) { -// listener.onRefresh(); -// } -// } + } else if (data instanceof CarConfigResp) { - if (listener != null) { - listener.onRefresh(); + if (listener != null && Constants.TITLE.TITLE_CAR_CONFIG_RESP.equals(listener.first)) { + listener.second.onRefresh(); } -// if (!listeners.isEmpty()) { -// for (OnAdasClientListener listener : listeners) { -// listener.onRefresh(); -// } -// } } else if (data instanceof RecordPanel) { listRecordPanel.add(0, time + str); if (listRecordPanel.size() > LIST_SIZE) { listRecordPanel.remove(listRecordPanel.size() - 1); } - if (listener != null) { - listener.onRefresh(); + if (listener != null && Constants.TITLE.RECEIVE_RECORD_RESULT.equals(listener.first)) { + listener.second.onRefresh(); } -// if (!listeners.isEmpty()) { -// for (OnAdasClientListener listener : listeners) { -// listener.onRefresh(); -// } -// } } else if (data instanceof GlobalPathResp) { listGlobalPathResp.add(0, time + str); if (listGlobalPathResp.size() > LIST_SIZE) { listGlobalPathResp.remove(listGlobalPathResp.size() - 1); } - if (listener != null) { - listener.onRefresh(); + if (listener != null && Constants.TITLE.RECEIVE_GLOBAL_PATH_RESP.equals(listener.first)) { + listener.second.onRefresh(); } -// if (!listeners.isEmpty()) { -// for (OnAdasClientListener listener : listeners) { -// listener.onRefresh(); -// } -// } } else if (data instanceof Warn) { listWarn.add(0, time + str); if (listWarn.size() > LIST_SIZE) { listWarn.remove(listWarn.size() - 1); } - if (listener != null) { - listener.onRefresh(); + if (listener != null && Constants.TITLE.RECEIVE_WARN.equals(listener.first)) { + listener.second.onRefresh(); } -// if (!listeners.isEmpty()) { -// for (OnAdasClientListener listener : listeners) { -// listener.onRefresh(); -// } -// } } else if (data instanceof ArrivalNotification) { listArrivalNotification.add(0, time + str); if (listArrivalNotification.size() > LIST_SIZE) { listArrivalNotification.remove(listArrivalNotification.size() - 1); } - if (listener != null) { - listener.onRefresh(); + if (listener != null && Constants.TITLE.RECEIVE_ARRIVAL_NOTIFICATION.equals(listener.first)) { + listener.second.onRefresh(); } -// if (!listeners.isEmpty()) { -// for (OnAdasClientListener listener : listeners) { -// listener.onRefresh(); -// } -// } } else if (data instanceof ErrorData) { listErrorData.add(0, time + str); if (listErrorData.size() > 100) { listErrorData.remove(listErrorData.size() - 1); } - if (listener != null) { - listener.onRefresh(); + if (listener != null && Constants.TITLE.RECEIVE_ERROR.equals(listener.first)) { + listener.second.onRefresh(); } -// if (!listeners.isEmpty()) { -// for (OnAdasClientListener listener : listeners) { -// listener.onRefresh(); -// } -// } } - return temp; } diff --git a/app_ipc_monitoring/src/main/java/com/zhidao/adas/client/base/BaseFragment.java b/app_ipc_monitoring/src/main/java/com/zhidao/adas/client/base/BaseFragment.java index bdb8d6d1c5..0516493d30 100644 --- a/app_ipc_monitoring/src/main/java/com/zhidao/adas/client/base/BaseFragment.java +++ b/app_ipc_monitoring/src/main/java/com/zhidao/adas/client/base/BaseFragment.java @@ -3,6 +3,7 @@ package com.zhidao.adas.client.base; import android.os.Bundle; import android.os.Handler; import android.os.Message; +import android.util.Pair; import android.view.View; import androidx.annotation.NonNull; @@ -42,17 +43,25 @@ public abstract class BaseFragment extends Fragment { initHandler(); } - @Override public void onResume() { super.onResume(); - DataDistribution.getInstance().registerOnAdasClientListener(adasClientListener); + if (!isHidden()) + DataDistribution.getInstance().registerAdasClientListener(new Pair<>(title, adasClientListener)); } @Override public void onPause() { super.onPause(); -// DataDistribution.getInstance().unregisterOnAdasClientListener(adasClientListener); + DataDistribution.getInstance().unregisterAdasClientListener(new Pair<>(title, adasClientListener)); + } + + @Override + public void onHiddenChanged(boolean hidden) { + super.onHiddenChanged(hidden); + if (!hidden) { + DataDistribution.getInstance().registerAdasClientListener(new Pair<>(title, adasClientListener)); + } } @@ -185,7 +194,8 @@ public abstract class BaseFragment extends Fragment { protected void handleMessage(Message msg) { switch (msg.what) { case WHAT_REFRESH: - onRefreshView(); + if (isVisible()) + onRefreshView(); break; } } diff --git a/app_ipc_monitoring/src/main/java/com/zhidao/adas/client/ui/AutopilotConfigActivity.java b/app_ipc_monitoring/src/main/java/com/zhidao/adas/client/ui/AutopilotConfigActivity.java index 31bff8d2bc..c49b03a313 100644 --- a/app_ipc_monitoring/src/main/java/com/zhidao/adas/client/ui/AutopilotConfigActivity.java +++ b/app_ipc_monitoring/src/main/java/com/zhidao/adas/client/ui/AutopilotConfigActivity.java @@ -4,6 +4,7 @@ import android.app.AlertDialog; import android.content.DialogInterface; import android.os.Bundle; import android.os.Message; +import android.util.Pair; import android.view.KeyEvent; import android.view.Menu; import android.view.MenuItem; @@ -20,17 +21,16 @@ import androidx.recyclerview.widget.DividerItemDecoration; import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.RecyclerView; +import com.zhidao.adas.client.DataDistribution; import com.zhidao.adas.client.OnAdasClientListener; import com.zhidao.adas.client.R; import com.zhidao.adas.client.adapter.AutopilotConfigAdapter; import com.zhidao.adas.client.base.BaseActivity; -import com.zhidao.adas.client.bean.UpdateDataEvent; import com.zhidao.adas.client.bean.AutoPilotMode; -import com.zhidao.adas.client.DataDistribution; +import com.zhidao.adas.client.bean.UpdateDataEvent; import com.zhidao.adas.client.utils.Constants; import com.zhidao.support.adas.high.common.ThreadPoolManager; - import org.greenrobot.eventbus.EventBus; import java.util.List; @@ -38,6 +38,7 @@ import java.util.List; public class AutopilotConfigActivity extends BaseActivity { private static final String TAG = "CreateActivity"; + private Pair listenerPair; private TextView toolbar_title; private RecyclerView recyclerView; private AutopilotConfigAdapter autopilotConfigAdapter; @@ -66,6 +67,7 @@ public class AutopilotConfigActivity extends BaseActivity { getSupportActionBar().setHomeButtonEnabled(true); //设置返回键可用 initHandler(); initRecyclerView(); + listenerPair = new Pair<>(Constants.TITLE.RECEIVE_GNSS_INFO, listener); toolbar_title.setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View v) { @@ -80,6 +82,15 @@ public class AutopilotConfigActivity extends BaseActivity { protected void onResume() { super.onResume(); init(); + DataDistribution.getInstance().registerAdasClientListener(new Pair<>(Constants.TITLE.RECEIVE_GNSS_INFO, listener)); + + } + + @Override + protected void onPause() { + super.onPause(); + DataDistribution.getInstance().unregisterAdasClientListener(new Pair<>(Constants.TITLE.RECEIVE_GNSS_INFO, listener)); + } private void init() { @@ -148,17 +159,6 @@ public class AutopilotConfigActivity extends BaseActivity { ThreadPoolManager.getsInstance().execute(runnable); } - @Override - protected void onStart() { - super.onStart(); - DataDistribution.getInstance().registerOnAdasClientListener(listener); - } - - @Override - protected void onStop() { - super.onStop(); -// DataDistribution.getInstance().unregisterOnAdasClientListener(listener); - } @Override protected void onDestroy() { diff --git a/app_ipc_monitoring/src/main/java/com/zhidao/adas/client/ui/MainActivity.java b/app_ipc_monitoring/src/main/java/com/zhidao/adas/client/ui/MainActivity.java index 6f6c44239f..1268018481 100644 --- a/app_ipc_monitoring/src/main/java/com/zhidao/adas/client/ui/MainActivity.java +++ b/app_ipc_monitoring/src/main/java/com/zhidao/adas/client/ui/MainActivity.java @@ -32,6 +32,7 @@ import android.widget.Toast; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.widget.AppCompatButton; +import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentTransaction; import androidx.recyclerview.widget.GridLayoutManager; @@ -134,7 +135,7 @@ public class MainActivity extends BaseActivity implements OnAdasListener, OnAdas private final List titleBtnData = new ArrayList<>(); private final List connectStatusList = new ArrayList<>(); private InfoTitleAdapter fragmentAdapter; - private final Map fragmentList = new HashMap<>(); + private Fragment fromFragment; private FragmentManager manager; private String ftpTime; private boolean isPad; @@ -508,18 +509,18 @@ public class MainActivity extends BaseActivity implements OnAdasListener, OnAdas titleFragmentData.add(Constants.TITLE.RECEIVE_GNSS_INFO); titleFragmentData.add(Constants.TITLE.RECEIVE_TRAJECTORY); titleFragmentData.add(Constants.TITLE.RECEIVE_TRACKED_OBJECTS); - titleFragmentData.add(Constants.TITLE.RECEIVE_POINT_CLOUD); - titleFragmentData.add(Constants.TITLE.RECEIVE_PLANNING_OBJECTS); titleFragmentData.add(Constants.TITLE.RECEIVE_VEHICLE_STATE); titleFragmentData.add(Constants.TITLE.RECEIVE_AUTOPILOT_STATE); titleFragmentData.add(Constants.TITLE.RECEIVE_REPORT_MESSAGE); titleFragmentData.add(Constants.TITLE.RECEIVE_PERCEPTION_TRAFFIC_LIGHT); titleFragmentData.add(Constants.TITLE.RECEIVE_PREDICTION_OBSTACLE_TRAJECTORY); + titleFragmentData.add(Constants.TITLE.RECEIVE_POINT_CLOUD); + titleFragmentData.add(Constants.TITLE.RECEIVE_PLANNING_OBJECTS); titleFragmentData.add(Constants.TITLE.RECEIVE_CAR_CONFIG_RESP); titleFragmentData.add(Constants.TITLE.RECEIVE_RECORD_RESULT); titleFragmentData.add(Constants.TITLE.RECEIVE_GLOBAL_PATH_RESP); - titleFragmentData.add(Constants.TITLE.RECEIVE_WARN); titleFragmentData.add(Constants.TITLE.RECEIVE_ARRIVAL_NOTIFICATION); + titleFragmentData.add(Constants.TITLE.RECEIVE_WARN); titleFragmentData.add(Constants.TITLE.RECEIVE_ERROR); @@ -959,21 +960,27 @@ public class MainActivity extends BaseActivity implements OnAdasListener, OnAdas } private void showFragment(String title) { - BaseFragment fragment = fragmentList.get(title); - if (fragment == null) { + Fragment to = manager.findFragmentByTag(title); + if (to == null) { if (Constants.TITLE.RECEIVE_CAR_CONFIG_RESP.equals(title)) { - AdasManager.getInstance().sendCarConfigReq(); - fragment = new VersionFragment("工控机版本\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t固定IP配置"); + to = new VersionFragment(Constants.TITLE.TITLE_CAR_CONFIG_RESP); } else { - fragment = new InfoFragment(title); + to = new InfoFragment(title); } - fragmentList.put(title, fragment); } - if (!fragment.isVisible()) { - FragmentTransaction transaction = manager.beginTransaction(); - transaction.replace(R.id.fl_info, fragment); - transaction.commit(); + FragmentTransaction transaction = manager.beginTransaction(); + if (fromFragment != null) { + transaction.hide(fromFragment); } + if (!to.isAdded()) { + transaction.add(R.id.fl_info, to, title).commit(); + } else { + transaction.show(to).commit(); + } + if (Constants.TITLE.RECEIVE_CAR_CONFIG_RESP.equals(title)) { + AdasManager.getInstance().sendCarConfigReq(); + } + fromFragment = to; } @Override diff --git a/app_ipc_monitoring/src/main/java/com/zhidao/adas/client/ui/VersionFragment.java b/app_ipc_monitoring/src/main/java/com/zhidao/adas/client/ui/VersionFragment.java index 8c99e0ad58..a150e0619c 100644 --- a/app_ipc_monitoring/src/main/java/com/zhidao/adas/client/ui/VersionFragment.java +++ b/app_ipc_monitoring/src/main/java/com/zhidao/adas/client/ui/VersionFragment.java @@ -25,6 +25,7 @@ import com.zhidao.adas.client.base.BaseFragment; import com.zhidao.adas.client.bean.Config; import com.zhidao.support.adas.high.AdasManager; import com.zhidao.support.adas.high.bean.VersionCompatibility; +import com.zhidao.support.adas.high.common.Constants; import com.zhidao.support.adas.high.common.CupidLogUtils; import java.text.SimpleDateFormat; @@ -163,7 +164,7 @@ public class VersionFragment extends BaseFragment { temp = ip + ":" + AdasManager.getInstance().getIpcConnectedPort(); } MessagePad.CarConfigResp adasConfig = AdasManager.getInstance().getCarConfig(); - list.add(new Config("更新时间:", sdf.format(new Date()))); + list.add(new Config("更新时间:", AdasManager.getInstance().getIpcConnectionStatus() == Constants.IPC_CONNECTION_STATUS.CONNECTED ? sdf.format(new Date()) : null)); list.add(new Config("工控机IP:", temp)); list.add(new Config("工控机版本:", adasConfig == null ? null : adasConfig.getDockVersion())); list.add(new Config("车牌号:", adasConfig == null ? null : adasConfig.getPlateNumber())); diff --git a/app_ipc_monitoring/src/main/java/com/zhidao/adas/client/utils/Constants.java b/app_ipc_monitoring/src/main/java/com/zhidao/adas/client/utils/Constants.java index 0392d46974..190534fcc3 100644 --- a/app_ipc_monitoring/src/main/java/com/zhidao/adas/client/utils/Constants.java +++ b/app_ipc_monitoring/src/main/java/com/zhidao/adas/client/utils/Constants.java @@ -155,6 +155,7 @@ public class Constants { String RECEIVE_PLANNING_OBJECTS = "Planning障碍物"; // String RECEIVE_BASIC_INFO_REQ = "自动驾驶设备基础信息请求"; + String TITLE_CAR_CONFIG_RESP = "工控机版本\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t固定IP配置"; String RECEIVE_CAR_CONFIG_RESP = "信息与配置"; String RECEIVE_RECORD_RESULT = "数据采集结果"; String RECEIVE_GLOBAL_PATH_RESP = "自动驾驶路径"; diff --git a/app_ipc_monitoring/src/main/java/com/zhidao/adas/client/utils/CrashHandler.java b/app_ipc_monitoring/src/main/java/com/zhidao/adas/client/utils/CrashHandler.java new file mode 100644 index 0000000000..86a38c657a --- /dev/null +++ b/app_ipc_monitoring/src/main/java/com/zhidao/adas/client/utils/CrashHandler.java @@ -0,0 +1,291 @@ +package com.zhidao.adas.client.utils; + +import android.content.Context; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.os.Build; +import android.os.Environment; +import android.os.Looper; +import android.util.Log; +import android.widget.Toast; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.io.Writer; +import java.lang.Thread.UncaughtExceptionHandler; +import java.lang.reflect.Field; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + + +/** + * ClassName: CrashHandler + * Function: UncaughtException处理类,当程序发生Uncaught异常的时候,由该类来接管程序,并记录发送错误报告. + * + * @author Norris Norris.sly@gmail.com + * @Date 2013 2013-3-24 下午12:27:10 + * @Fields ────────────────────────────────────────────────────────────────────────────────────────────────────── + * @Methods ────────────────────────────────────────────────────────────────────────────────────────────────────── + * 2013-3-24下午12:27:10 Modified By Norris + * ────────────────────────────────────────────────────────────────────────────────────────────────────── + * @see ────────────────────────────────────────────────────────────────────────────────────────────────────── + * @since Ver 1.0 I used to be a programmer like you, then I took an arrow in the knee + */ +public class CrashHandler implements UncaughtExceptionHandler { + private static final String FILE_PATH = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "IPCMonitoring" + File.separator + "Crash" + File.separator;//程序外部存储跟目录 + /** + * Log日志的tag + * String : TAG + * + * @since 2013-3-21下午8:44:28 + */ + private static final String TAG = "NorrisInfo"; + /** + * 系统默认的UncaughtException处理类 + * Thread.UncaughtExceptionHandler : mDefaultHandler + * + * @since 2013-3-21下午8:44:43 + */ + private UncaughtExceptionHandler mDefaultHandler; + /** + * CrashHandler实例 + * CrashHandler : mInstance + * + * @since 2013-3-21下午8:44:53 + */ + private static CrashHandler mInstance = new CrashHandler(); + /** + * 程序的Context对象 + * Context : mContext + * + * @since 2013-3-21下午8:45:02 + */ + private Context mContext; + /** + * 用来存储设备信息和异常信息 + * Map : mLogInfo + * + * @since 2013-3-21下午8:46:15 + */ + private final Map mLogInfo = new HashMap(); + /** + * 用于格式化日期,作为日志文件名的一部分(FIXME 注意在windows下文件名无法使用:等符号!) + * SimpleDateFormat : mSimpleDateFormat + * + * @since 2013-3-21下午8:46:39 + */ + private SimpleDateFormat mSimpleDateFormat = new SimpleDateFormat("yyyyMMdd_HH-mm-ss"); + + /** + * Creates a new instance of CrashHandler. + */ + private CrashHandler() { + } + + /** + * getInstance:{获取CrashHandler实例 ,单例模式 } + * ────────────────────────────────── + * + * @return CrashHandler + * @throws + * @since I used to be a programmer like you, then I took an arrow in the knee Ver 1.0 + * ────────────────────────────────────────────────────────────────────────────────────────────────────── + * 2013-3-21下午8:52:24 Modified By Norris + * ────────────────────────────────────────────────────────────────────────────────────────────────────── + */ + public static CrashHandler getInstance() { + return mInstance; + } + + /** + * init:{初始化} + * ────────────────────────────────── + * + * @param paramContext + * @return void + * @throws + * @since I used to be a programmer like you, then I took an arrow in the knee Ver 1.0 + * ────────────────────────────────────────────────────────────────────────────────────────────────────── + * 2013-3-21下午8:52:45 Modified By Norris + * ────────────────────────────────────────────────────────────────────────────────────────────────────── + */ + public void init(Context paramContext) { + mContext = paramContext; + // 获取系统默认的UncaughtException处理器 + mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler(); + // 设置该CrashHandler为程序的默认处理器 + Thread.setDefaultUncaughtExceptionHandler(this); + } + + /** + * 当UncaughtException发生时会转入该重写的方法来处理 + * (non-Javadoc) + * + * @see UncaughtExceptionHandler#uncaughtException(Thread, Throwable) + */ + public void uncaughtException(Thread paramThread, Throwable paramThrowable) { + // 获取设备参数信息 + getDeviceInfo(mContext); +// mLogInfo.put("versionName", String.valueOf(BuildConfig.VERSION_CODE)); +// mLogInfo.put("versionCode", BuildConfig.VERSION_NAME); + // 保存日志文件 + String result = saveCrashLogToFile(paramThrowable); + mDefaultHandler.uncaughtException(paramThread, paramThrowable); +// if (!handleException(paramThrowable) && mDefaultHandler != null) { +// // 如果自定义的没有处理则让系统默认的异常处理器来处理 +// mDefaultHandler.uncaughtException(paramThread, paramThrowable); +// } else { +//// try { +//// // 如果处理了,让程序继续运行1秒再退出,保证文件保存并上传到服务器 +//// paramThread.sleep(1000); +//// } catch (InterruptedException e) { +//// e.printStackTrace(); +//// } +// // 退出程序 +// android.os.Process.killProcess(android.os.Process.myPid()); +// System.exit(1); +// } + } + + /** + * handleException:{自定义错误处理,收集错误信息 发送错误报告等操作均在此完成.} + * ────────────────────────────────── + * + * @param paramThrowable + * @return true:如果处理了该异常信息;否则返回false. + * @throws + * @since I used to be a programmer like you, then I took an arrow in the knee Ver 1.0 + * ────────────────────────────────────────────────────────────────────────────────────────────────────── + * 2013-3-24下午12:28:53 Modified By Norris + * ────────────────────────────────────────────────────────────────────────────────────────────────────── + */ + public boolean handleException(Throwable paramThrowable) { + if (paramThrowable == null) + return false; + new Thread() { + public void run() { + Looper.prepare(); + Toast.makeText(mContext, "很抱歉,程序出现异常,即将退出", Toast.LENGTH_SHORT).show(); + Looper.loop(); + } + }.start(); + // 获取设备参数信息 + getDeviceInfo(mContext); + // 保存日志文件 + String result = saveCrashLogToFile(paramThrowable); + //上传崩溃日志 + return true; + } + + /** + * getDeviceInfo:{获取设备参数信息} + * ────────────────────────────────── + * + * @param paramContext + * @throws + * @since I used to be a programmer like you, then I took an arrow in the knee Ver 1.0 + * ────────────────────────────────────────────────────────────────────────────────────────────────────── + * 2013-3-24下午12:30:02 Modified By Norris + * ────────────────────────────────────────────────────────────────────────────────────────────────────── + */ + public void getDeviceInfo(Context paramContext) { + try { + // 获得包管理器 + PackageManager mPackageManager = paramContext.getPackageManager(); + // 得到该应用的信息,即主Activity + PackageInfo mPackageInfo = mPackageManager.getPackageInfo( + paramContext.getPackageName(), PackageManager.GET_ACTIVITIES); + if (mPackageInfo != null) { + String versionName = mPackageInfo.versionName == null ? "null" + : mPackageInfo.versionName; + String versionCode = mPackageInfo.versionCode + ""; + mLogInfo.put("versionName", versionName); + mLogInfo.put("versionCode", versionCode); + } + } catch (NameNotFoundException e) { + e.printStackTrace(); + } + // 反射机制 + Field[] mFields = Build.class.getDeclaredFields(); + // 迭代Build的字段key-value 此处的信息主要是为了在服务器端手机各种版本手机报错的原因 + for (Field field : mFields) { + try { + field.setAccessible(true); + mLogInfo.put(field.getName(), Objects.requireNonNull(field.get("")).toString()); + } catch (IllegalArgumentException | IllegalAccessException e) { + e.printStackTrace(); + } + } + } + + /** + * saveCrashLogToFile:{将崩溃的Log保存到本地} + * TODO 可拓展,将Log上传至指定服务器路径 + * ────────────────────────────────── + * + * @param paramThrowable + * @return FileName + * @throws + * @since I used to be a programmer like you, then I took an arrow in the knee Ver 1.0 + * ────────────────────────────────────────────────────────────────────────────────────────────────────── + * 2013-3-24下午12:31:01 Modified By Norris + * ────────────────────────────────────────────────────────────────────────────────────────────────────── + */ + + + private String saveCrashLogToFile(Throwable paramThrowable) { + StringBuffer mStringBuffer = new StringBuffer(); + for (Map.Entry entry : mLogInfo.entrySet()) { + String key = entry.getKey(); + String value = entry.getValue(); + mStringBuffer.append(key + "=" + value + "\r\n"); + } + Writer mWriter = new StringWriter(); + PrintWriter mPrintWriter = new PrintWriter(mWriter); + paramThrowable.printStackTrace(mPrintWriter); + paramThrowable.printStackTrace(); + Throwable mThrowable = paramThrowable.getCause(); + // 迭代栈队列把所有的异常信息写入writer中 + while (mThrowable != null) { + mThrowable.printStackTrace(mPrintWriter); + // 换行 每个个异常栈之间换行 + mThrowable = mThrowable.getCause(); + } + + //记得关闭 + + String mResult = mWriter.toString(); + // String mResult = mWriter.toString().replace("\n", "").replace(":", "").replace("/", "").replace("\t", ""); + mStringBuffer.append(mResult); + // 保存文件,设置文件名 + String mTime = mSimpleDateFormat.format(new Date()); + String mFileName = mContext.getPackageName() + "_Exception-" + mTime + ".log"; + if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { + try { + File mDirectory = new File(FILE_PATH); + Log.v(TAG, mDirectory.toString()); + if (!mDirectory.exists()) + mDirectory.mkdirs(); + FileOutputStream mFileOutputStream = new FileOutputStream(mDirectory + File.separator + mFileName); + mFileOutputStream.write(mStringBuffer.toString().getBytes()); + mFileOutputStream.close(); + return mResult; + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + return mResult; + } + + +}