Android应用内安装异常处理

1.权限

2.配置FileProvider
filepath中xml内容参照:




3.检查权限:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
boolean hasInstallPermission = getPackageManager().canRequestPackageInstalls();
if (!hasInstallPermission) {
startInstallPermissionSettingActivity();
}
}


@RequiresApi(api = Build.VERSION_CODES.O)
private void startInstallPermissionSettingActivity() {

Uri packageURI = Uri.parse("package:" + BuildConfig.APPLICATION_ID);
//注意这个是8.0新API
Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, packageURI);
startActivityForResult(intent, 1);

}

4.安装
private static void apkIntentInstallNew(File apkFile,Context mContext){
Intent intent = null;
if (Build.VERSION.SDK_INT > 29){
intent = new Intent();
}else{
intent = new Intent(“android.intent.action.VIEW”);
}//这个地方需要注意
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//Uri uri = Uri.fromFile(apkFile);
Uri uri = null;
//todo N FileProvider
//todo O install permission
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){
uri = FileProvider.getUriForFile(mContext,”应用包名.fileProvider”, apkFile);//file不用在这里new,直接在外边new然后传进来
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
}else{
uri = Uri.fromFile(apkFile);
}
intent.setDataAndType(uri, “application/vnd.android.package-archive”);
mContext.startActivity(intent);
}

Android ListView中TextWatcher问题

ListView中给每项的Edittext添加TextWatcher事件碰到问题:

当某一下edittext输入值触发afterTextChanged时候会触发其他项中textwatcher的aftertextchange事件。
修改前:
修改后:
处理方法:
将textwatcher和holder绑定,只在初始化holder的时候初始化textwatcher,然后在每项单独设置textwatcher需要绑定的数据。

Android 键盘输入表情和颜文字禁用


public class EmojiExcludeFilter implements InputFilter {

   @Override
   public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
      boolean hasManyType = false;
      int sourcelen = 0;
      for (int i = start; i < end; i++) {
         int type = Character.getType(source.charAt(i));
         Log.e("InputFilter","text:"+source.toString()+","+type);
         if (type == Character.SURROGATE || type == Character.OTHER_SYMBOL) {
            Log.e("InputFilter","text 133");
            return "";
         }
         sourcelen ++;
         if(type!=Character.UPPERCASE_LETTER&&type!=Character.BYTES&&type!=Character.DECIMAL_DIGIT_NUMBER&&type!=Character.OTHER_LETTER&&type!=Character.SPACE_SEPARATOR){
            hasManyType = true;
         }
      }

      if("◽".equals(source.toString())||"◼".equals(source.toString())||"◻".equals(source.toString())){
         Log.e("InputFilter","text:143");
         return "";
      }
      if("〰".equals(source.toString())||"〽".equals(source.toString())||"⤵".equals(source.toString())){
         Log.e("InputFilter","text:147");
         return "";
      }
      if("⤴".equals(source.toString())||"◾".equals(source.toString())||"⤵".equals(source.toString())){
         Log.e("InputFilter","text:151");
         return "";
      }
      if(hasManyType&&sourcelen>1){//处理
         Log.e("InputFilter","text:155:"+source.toString());
         return "";
      }

      Log.e("InputFilter","text:"+source.toString()+","+source.toString());
      return null;
   }
}

表情的type是单一数值可以直接筛选;

颜文字的type是有多种类型拼接起来的:常用字符(type:2),数值(type:9),中文(5),空格以及其他特殊字符组成,并且颜文字的长度肯定大于1。如果字符长度大于1并且包含特殊字符,可以判定该次输入为颜文字。

Android 系统录制屏幕

如果依赖版本是Android Q之后的版本,系统录制屏幕的方法需要有所变动:

1、需要将onActivityResult中返回的resultcode和data传给service,然后在service中获取MediaProjcection

MediaProjection mediaProjection = ScreenRecorderCtrl.getInstance().getProjectionManager().getMediaProjection(resultcode, data)

2、Mainfest对应service中添加如下属性

    android:enabled="true"
    android:foregroundServiceType="mediaProjection"

3、Service中onStartCommand中需要设置通知,该方法要在MediaProjection初始化之前

/**
 * 添加一个状态栏通知
 */
private void addNotification(){

    NotificationManager manager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
    if(Build.VERSION.SDK_INT >= 26)
    {
        //当sdk版本大于26
        String id = "screenrecord";
        String description = "143";
        int importance = NotificationManager.IMPORTANCE_HIGH;
        NotificationChannel channel = new NotificationChannel(id, description, importance);
        manager.createNotificationChannel(channel);
        Notification notification = new Notification.Builder(this, id)
                .setCategory(Notification.CATEGORY_MESSAGE)
                .setSmallIcon(R.drawable.recoding)
                .setContentTitle("录屏服务 ")
                .setContentText("进行中")
                .setAutoCancel(false)
                .build();
        startForeground(1,notification);
    }
    else
    {
        Notification notification = new Notification.Builder(this)
                .setContentTitle("录屏服务 ")
                .setContentText("进行中")
                .setSmallIcon(R.drawable.recoding)
                .build();
        startForeground(1, notification);
    }
}

4、Service启动的时候

Intent service = new Intent(this, ScreenRecorderService.class);
service.putExtra("code", resultcode);//onactivityresult返回值
service.putExtra("data", data);//onactivityresult 返回值
if (android.os.Build.VERSION.SDK_INT <=26) {
    startService(service);
}else {
    startForegroundService(service);
}

Android Wi-Fi连接

1、Android Q之后Wi-Fi连接要调用,如果用之前的方法无法连接对应的网络

@RequiresApi(api = Build.VERSION_CODES.Q)
    public void wifiConnectAndroidQ(String ssid, String password)
    {

            NetworkSpecifier specifier =
                    new WifiNetworkSpecifier.Builder()
                            .setSsidPattern(new PatternMatcher(ssid, PatternMatcher.PATTERN_PREFIX))
                            .setWpa2Passphrase(password)
                            .build();

            NetworkRequest request =
                    new NetworkRequest.Builder()
                            .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
                            .removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
                            .setNetworkSpecifier(specifier)
                            .build();

            ConnectivityManager connectivityManager = (ConnectivityManager)
                    getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE);

            ConnectivityManager.NetworkCallback networkCallback = new ConnectivityManager.NetworkCallback() {
                @Override
                public void onAvailable(Network network) {
                    // do success processing here..
//                    Log.e("MainActivity","onavaliable:"+wifiManager.getConnectionInfo().);
                }

                @Override
                public void onUnavailable() {
                    // do failure processing here..
                }
            };
            connectivityManager.requestNetwork(request, networkCallback);
            // Release the request when done.
            // connectivityManager.unregisterNetworkCallback(networkCallback);

    }

2、AndroidQ之后判断网络是否连接

private boolean isWifiEnable() {
    ConnectivityManager cm = (ConnectivityManager) getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE);
    if (Build.VERSION.SDK_INT>=29) {
        if (cm != null) {
            NetworkCapabilities nc = cm.getNetworkCapabilities(cm.getActiveNetwork());
            boolean boo = nc != null && nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) && nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) && nc.hasTransport(NetworkCapabilities.TRANSPORT_WIFI);
            Log.e("MainActivity","iswifienable:"+boo);
            return boo;
        } else {
            Log.e("MainActivity","iswifienable:"+false);
            return false;
        }
    } else {
        boolean boo = cm != null && cm.getActiveNetworkInfo() != null && cm.getActiveNetworkInfo().getType() == ConnectivityManager.TYPE_WIFI;
        Log.e("MainActivity","iswifienable:"+boo);
        return boo;
    }
}

3、如果当前连接的Wi-Fi在系统Wi-Fi设置中取消保存,应用能接收到Wi-Fi变动,但是下面方法返回的ssid还是取消保存的Wi-Fi,

wifiInfo.getSSID()

需要再判断下下面是不是-1

wifiInfo.getNetworkId()

4、Android Q之后禁用三方应用打开和关闭Wi-Fi

Android WebView

1、web view黑屏问题:
1)硬件加速改成软件加速;2)setJavascriptEnable;3)webview布局尺寸设置为充满整个屏幕;4)如果是dialog弹窗中显示,则最好设置dialog的尺寸大小(window设置宽高)

2、字体设置问题:setTextZoom(),这个默认是100(原大小),另外在list view中嵌套的时候要注意。

Android webview与Javascript

js中调用安卓方法。
mWebView.addJavascriptInterface(new JsInterface(),”EdutechcommWv”);
mWebView.getSettings().setJavaScriptEnabled(true);
public class JsInterface{

public JsInterface() {
}

@JavascriptInterface
public void getPlatformIp(){
mWebView.loadUrl(“javascript:window.API_ROOT = “+ip+”;”);//注入JS,JS的window直接获取数据信息
return;
}

@JavascriptInterface
public String getUserInfo(){
return “userinfo”;//直接返回数据,js获取处理
}
}

Android问题汇总

1.recycleview 只显示第一条数据
处理方法:item的父布局的高度设置为wrap-content
2.类似直播连麦,二个surfaceview播放直播流,需要大小窗口切换主视频,切换过程中会出现某个surfaceview透明显示不出的情况。
处理方法:setZOrderMediaOverlay设置小窗口为true,设置主窗口为false。小窗口布局层级在主窗口之上。
if(mVideoItemStudent!=null&&mVideoItemStudent.getSurfaceView()!=null){
mVideoItemStudent.getSurfaceView().setZOrderMediaOverlay(false);
}
if(mVideoItemTeacher!=null&&mVideoItemTeacher.getSurfaceView()!=null){
mVideoItemTeacher.getSurfaceView().setZOrderMediaOverlay(true);
}//luojie 二个surfaceview播放视频流,如果不只设置Zorder为true,在某些设备上可能会有某个surface会透明显示不出,
3.好的日历插件
implementation ‘com.haibin:calendarview:3.6.9’
4.targetVersion超过28,安卓不允许在根目录创建文件夹;
5.当Wi-Fi不能访问外网时,socket内网连接会有异常,处理办法如下:
private void useWifiNetWork(){
ConnectivityManager connection_manager =
(ConnectivityManager) getApplication().getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkRequest.Builder request = new NetworkRequest.Builder();
request.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
connection_manager.registerNetworkCallback(request.build(), new ConnectivityManager.NetworkCallback() {

@Override
public void onAvailable(Network network) {
ConnectivityManager.setProcessDefaultNetwork(network);
}
});
}
6.socket:通过在service中发心跳包保持连接,但是当手机休眠时socket会无法发送数据,可用的解决办法如下:
可以在oncreate中:
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, “xxx:xxx”);
wl.acquire();
在ondestroy中:
if(wl!=null){
wl.release();
}
acquire和release的次数必须是一致的,否则会有异常。
7.抓取摄像头视频流,然后将视频流数据同步给服务端,可用的方案有:
a.开启摄像头预览,抓取预览的图片数据YUV格式,将图片数据按照需求做下转换,将数据传递给mediacodec编码,将处理好的流数据通过协议发送给服务端。
b.相机将预览画面通过render渲染在GLSurfaceview,mediacodec设置inputsurface,然后去编码,再将处理好的数据通过协议发送给服务端。
8.相机预览模糊:设置previewsize是否设置的太小;相机自动对焦是否设置。