Android小提示四

简介: Android开发一些提示

@TOC

【56.Android P 9.0网络权限http】

解决方法:共四种
1、 如果一定要使用明文通信的话,则可以打开AndroidManifest.xml 文件,在 application 元素中添加:
android:usesCleartextTraffic="true"

  1. 为了安全,不建议上面的使用明文的通信方式,不过上面的这种方法可以作为一种临时的通信策略
  2. 如果声明不使用明文通信,则可以在application元素中添加:
    android:usesCleartextTraffic=”false”
    此声明指示该应用不使用明文网络通信,并使 Android Marshmallow 的平台网络堆栈禁止该应用中的明文通信。例如,如果您的应用意外尝试通过 HTTP 明文请求登录用户,该请求将被阻止,该用户的身份和密码信息不会泄露到网络上。

2、 项目改用https请求;
3、 项目的targetSdkVersion改为27以下;
4、 在res的xml目录下,新建一个xml文件(名称自定义,如 network_security_config.xml),内容如下:

<?xml version="1.0" encoding="utf-8"?> 
<network-security-config>     
  <base-config cleartextTrafficPermitted="true" /> 
</network-security-config>

在manifest清单文件配置application:

<application
...
 android:networkSecurityConfig="@xml/network_security_config"
...
    />

【57.布局分包】

模块build.gradle下添加代码,然后每个文件夹下需要有layout文件夹

android {
    。。。
    sourceSets {
        main{
            res.srcDirs=[
                    "src/main/res",  //这个意思是全部资源,包括mipmap等,必须
                    "src/main/res/layout",
                    "src/main/res/layout/practice4",
                    "src/main/res/layout/practice3",
            ]
        }
    }
}

【58.mHandler在activity警告 --】

其一:(Activity中)

private final MHandler mHandler = new MHandler(this);
 
private static class MHandler extends Handler {
    private final WeakReference<MainActivity> mActivity;
 
    public MHandler(MainActivity activity) {
        mActivity = new WeakReference<MainActivity>(activity);
    }
 
    @Override
    public void handleMessage(Message msg) {
        MainActivity activity = mActivity.get();
          if(activity!=null){
              
          }
    }
}

不规范的写法:
 private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
 
        };
    };
//另外一个办法
 private Handler mHandler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            //但是不能引用外部方法了
            return false;
        }
    });

其二:(自定义View中)

private TimerHandler mHandler;
 
//采用弱引用防止内存泄漏
private static final class TimerHandler extends Handler {
 
    private WeakReference<StudyView> mView;
 
    private TimerHandler(StudyView clockView) {
        mView= new WeakReference<>(clockView);
    }

    @Override
    public void handleMessage(Message msg) {
        StudyView view = mView.get();
        //isPlaying是StudyView里的变量
        if (view != null && view.isPlaying) {
            view.getTime();    //StudyView里的方法
            view.invalidate();//重新绘制
            sendEmptyMessageDelayed(1, 1000);//每1000毫秒一请求
        }
    }
}
原文链接:https://blog.csdn.net/qq_38363506/article/details/90903240

【其他警告】

@SuppressLint("SimpleDateFormat")
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-ddHH:mm:ss");  

//解决
SimpleDateFormat newSimpleDateFormat = new SimpleDateFormat(
            "yyyy年MM月dd日HH时mm分", Locale.getDefault());
@SuppressWarnings(“rawtypes”)和@SuppressWarnings({ “unchecked”, “rawtypes” }) 
不规范写法:Class clazz = Class.forName(“android.view.Display”); 
正确写法:Class<?> clazz = Class.forName("android.view.Display");

【59.shape划线注意事项】

line划线时注意一下几点:

  • 只能画水平线,画不了竖线;
  • 线的高度是通过stroke的android:width属性设置的;
  • size的android:height属性定义的是整个形状区域的高度;
  • size的height必须大于stroke的width,否则,线无法显示;
  • 线在整个形状区域中是居中显示的;
  • 线左右两边会留有空白间距,线越粗,空白越大;
  • 引用虚线的view需要添加属性android:layerType,值设为"software",否则显示不了虚线。
<shape xmlns:android="http://schemas.android.com/apk/res/android"  
    android:shape="line">  
    <stroke  
        android:width="1dp"  
        android:color="#ff0000"/>  
    <size  
        android:height="4dp"/>  
</shape> 

<shape xmlns:android="http://schemas.android.com/apk/res/android"  
    android:shape="line">  
    <solid  
        android:color="#990000ff"/>  
    <stroke  
        android:width="1dp"  
        android:color="#00ff00"  
        android:dashWidth="4dp"  
        android:dashGap="4dp"/>  
</shape> 

【60.异或加解密】

//java.test里测试  加密与解密的代码相同
int pwd = 9;
String normal = "~!@#$%^&*()_+=-0";
//加密
int len = normal.length();
StringBuilder bui=new StringBuilder();

for(int i=0;i<len;i++){
    System.out.println("char:"+normal.charAt(i));
   int res=normal.charAt(i)^pwd;
   bui.append((char)res);  //强转为ASCII字符 
}
System.out.println("加密后:"+bui.toString());

System.out.println("-------下面代码跟上面一样--------------");

String cry=bui.toString();

//解密
int len = cry.length();
StringBuilder bui=new StringBuilder();

for(int i=0;i<len;i++){
    System.out.println("char:"+cry.charAt(i));
   int res=cry.charAt(i)^pwd;
   bui.append((char)res);  //强转为ASCII字符 
}
System.out.println("解密后:"+bui.toString());

【61。onPageScrollStateChanged的三个状态】

viewPager.addOnPageChangeListener(new ViewPager.OnPagerChangeListener(){
    public void onPageScrolled(int position, float offset, int pix){
        //参数2:偏移量0-1,滑动到一半可以用0.5 标识
        //参数3:分辨率,比如1080P的,则就是0-1079
    }
    
    public void onPageSelected(int position){
        //停止滑动后的位置
    }
    
    public void onPageScrollStateChanged(int arg0){
        参数arg0有三种取值:
        0:什么都没做
        1:开始滑动
        2:滑动结束
        打印了一下滑动过程的顺序:
        从滑动开始依次为:
        argo== (1,2,0)
    }
});

【62.Android转场动画】

https://www.jianshu.com/p/86ba2e1eb80c

【63.矢量图简介Vector】

1.两种方法来创建:

  • 1)右击drawable-->Drawable resource file-->设置root
    element为vector,这样的矢量图绘制逻辑完全掌握在开发者手里(自己绘制--》看下面的);
  • 2)右击drawable-->Vector
    Asset,选择SVG或者PSD文件直接生成根标签为vector的xml文件,怎样把png转换成SVG(可以用 阿里iconfont 或者
    [http://inloop.github.io/svg2android/]())。

作者:宛丘之上兮
链接:https://www.jianshu.com/p/0972a0d290e9
clipboard.png

  • width和height:当使用这个矢量图的View的宽高是wrap_content 的时候这两个属性才生效;
  • ewportWidth和viewportHeight:决定画布的宽高,是定义的一个虚拟空间,方便编辑pathData属性,如果pathData中的点超出了这个虚拟空间,超出的部分将不会展现给用户;虚拟空间的原点仍然还是在左上角(R点就是原点)。

简单的vector:[https://blog.csdn.net/qq_35323561/article/details/80018898]()

【64.权限请求6.0 M 以上动态】

//1.简单写法
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
    //两种方式
    String[] perms = {"android.permission.RECORD_AUDIO",Manifest.permission.WRITE_EXTERNAL_STORAGE};
    //因为是在Activity下,所以可以直接调用
    if(checkSelfPermission(perms[0]) == PackageManager.PERMISSION_DENIED ||
       checkSelfPermission(perms[1]) == PackageManager.PERMISSION_DENIED){
           //自己在onRequestPermissonsResult处理
           requestPermission(perms, 200);
          //ActivityCompat.requestPermissions(this,perms,200);
    } else {....}
}

//2.分开  [自己去封装工具类]
//应用
if(checkPermission()){
    //用户拥有权限
    ...
} else {
    //去请求
    requestPermission();
}

//检查权限
private boolean checkPermission(){
    int result = ContextCompat.checkSelfPermission(this,Manifest.permission.RECORD_AUDIO);
    //如果权限授予了
    if(result == PackageManager.PERMISSION_GRANTED){
        return true;
    } else {
        return false;
    }
}
//请求权限
private void requestPermission(){
    //第一次被拒后 或者之前允许又在设置去掉了 走这里,所以仍要请求权限(但是可以弹窗说名原因)
    if(ActivityCompat.shouldShowRequestPermissionRationale(this,perms[0])){
        toast("请在设置里允许权限");
        //我强制在弹窗请求
        ActivityCompat.requestPermission(this,new String[]{perms[0],perms[1]}, 1);
    } else {
        //请求权限(两种情况)
        //第一种:第一次请求权限
        ActivityCompat.requestPermission(this,new String[]{perms[0],perms[1]}, 1);
        
        //第二种:拒绝并选择“Never ask again”
    }
}
//接受权限结果
@override
public void onRequestPermissonsResult(...){
    if(requestCode == 1){
        if(grantResults.length>0 && grantResults[0]==PackageManager.PERMISSION_GRANTED){
            //申请成功
            。。。逻辑代码
        } else {
            //用户拒绝
        }
        return;
    }
    super.onRequestPermissonsResult(...);
}

clipboard1.png

shouldShowRequestPermissionRationable 方法会返回以下两种情况:

  • 返回true:
    用户之前在申请权限操作时,点击了“拒绝”按钮,但是没有选中“Never ask again”选项。
    处理方法—— 再次调用requestPermission方法申请权限。
  • 返回false:

    • 用户从来没有申请过此权限;
      处理方法—— 直接调用 requestPermission方法申请权限。
    • 用户之前选中拒绝,并勾选了“Never ask again”选项。
      处理方法—— 弹出自定义对话框,提示用户此操作必须通过权限申请之后才能继续使用此功能,并给用户提供进入权限设置界面的入口。

注意: shouldShowRequestPermissionRationable 返回true的情况

在国内很多手机厂商中设置了自动屏蔽,也就是没有返回true的情况,比如华为、小米等手机。

外例

public void requestPerm(View view){
    //版本高于23,需要动态申请
    if(shouldAskPerm){
        //判断是否已经授予权限
        if(ContextCompat.checkSelfPermission(this,Manifest.permission.WRITE_CONTACTS)
            != PackageManager.PERMISSION_GRANTED){
            //调用shouldShowRequestPer...判断用户之前的操作
            if(ActivityCompat.shouldShowRequestPR(this,Manifest.permission.WRITE_CONTACTS)){
                //用户再对话框中拒绝权限,并没有选中“Never ask again”
                ActivityCompat.requestPermission(this,new String[]{Manifest.permission.WRITE_CONTACTS},REQUEST_CODE);
                
            }else{
                //第一种:第一次请求权限
                ActivityCompat.requestPermission(this,new String[]{perms[0],perms[1]}, 1);
        
                //第二种:拒绝并选择“Never ask again”
                //这里需要再SharedPreference里设置第一次申请的操作,默认true,第一次申请后false
                //跳转到设置里,去手动设置允许
            }
        } else{
            //权限已申请,执行操作
            。。。
        }
    } else{
        //版本低于高于23
    }
}
public boolean shouldAskPerm(){
    return Build.VERSION.SDK_INT>=Build.VERSION_CODES.M;
}

checkSelfPermission 检查某权限是否已申请
requestPermissions 主动发送权限申请
shouldShowRequestPermissionRationale 判断用户之前对申请权限做出的动作

【65.RecyclerView刷新固定控件】--避免图片闪烁

https://blog.csdn.net/qq402164452/article/details/53464091

//注意这是三个参数的 onBindViewHolder
@Override
public void onBindViewHolder(ViewHolder holder, int position, List<Object> payloads) {
    //为空时,就是最初的,初始化
    if (payloads.isEmpty()) {
        onBindViewHolder(holder, position);
    } else {
        holder.container.setBackgroundColor(position == mCurrentPosition ? ContextCompat.getColor(activity,R.color.color_FF35BAF3) : Color.TRANSPARENT);
    }
}

【66.判断主子线程】

onCreate->onStart->onPostCreate->onResume->onPostResume
到onPostCreate时,Activity应该已经彻底跑起来了,这时可以测量View宽高

让View重绘, 需要先判断当前线程到底是不是主线程, 然后根据判断结果来决定到底是调用 invalidate() 还是 postInvalidate() 方法. 如果当前是主线程, 就调用 invalidate() 方法; 而如果当前是子线程, 就调用 postInvalidate() 方法, 注意: 子线程中不能调用 invalidate() 方法, 否则就会报异常, 提示我们不能在子线程中更新UI

//1.
public boolean isMainThread() {
    return Looper.getMainLooper() == Looper.myLooper();
}
//2.
public boolean isMainThread() {
    return Looper.getMainLooper().getThread() == Thread.currentThread();
}
//3.
public boolean isMainThread() {
    return Looper.getMainLooper().getThread().getId() == Thread.currentThread().getId();
}

Android中切换到主线程更新方法:
?? View.post()方法在android7.0之前,可能会不生效,在异步线程view.post方法不执行的情况居多。建议使用Handler post方法代替。但Android 7.0之后不管在主线程还是在子线程都可以成功执行view.post内部逻辑(https://blog.csdn.net/longlong2015/article/details/88826269
??在Android 7.0之后的手机上如果通过new创建的View,如果没有将它通过addView()加入到ViewGroup布局中,那通过View.post()发送出去的任务将不再执行,也就无法通过Viwe.post更新UI。

经验证7.0 以后 post可以执行,但是6.0却无法执行
在子线程中更新UI
//1.方法一: view.post(Runnable action)  【注意这个,在下面】
 textView.post(new Runnable() {
    @Override
    public void run() {
        textView.setText("更新textView");
    }
});
//view.postDelayed(Runnable action, long delayMillis)用来延迟发送。

//2.方法二:runOnUiThread(Runnable action)
注意:context 对象要是 主线程中的MainActivity,这样强转才可以。
runOnUiThread(new Runnable() {
    @Override
    public void run() {
        //此时已在主线程中,更新UI
    }
});

//3.方法三:Handler机制
首先在主线程中定义Handler,Handler mainHandler = new Handler();(必须要在主线程中定义才能操作主线程,
如果想在其他地方定义声明时要这样写
Handler mainHandler = new Handler(Looper.getMainLooper()),来获取主线程的 Looper 和 Queue )
获取到 Handler 后就很简单了,用handler.post(Runnable r)方法把消息处理放在该 handler 依附的消息队列中(也就是主线程消息队列)。

(1)假如该方法是在子线程中
Handler mainHandler = new Handler(Looper.getMainLooper());
mainHandler.post(new Runnable() {
    @Override
    public void run() {
        //已在主线程中,更新UI
    }
});
//Handler还有下面的方法:
//1.postAtTime(Runnable r, long uptimeMillis); //在某一时刻发送消息
//2.postAtDelayed(Runnable r, long delayMillis); //延迟delayMillis毫秒再发送消息

(2)假设在主线程中
Handler myHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        switch(msg.what) {
            case 0:
                //更新UI等
                break;
            default:
                break;
        }
    }
}
之后可以把 mainHandler 当做参数传递在各个类之间,当需要更新UI时,可以调用sendMessage一系列方法来执行handleMessage里的操作。

子线程中发送消息到主线程更新UI
/**
  *获取消息,尽量用obtainMessage()方法,查看源码发现,该方法节省内存。
  *不提倡用Messenger msg=new Messenger()这种方法,每次都去创建一个对象,肯定不节省内存啦!
  *至于为什么该方法还存在,估计还是有存在的必要吧。(留作以后深入研究)
  */
new Thread(new Runnable(){  
        @Override  
        public void run() {  
            //耗时操作,完成之后发送消息给Handler,完成UI更新;  
            mHandler.sendEmptyMessage(0);  

            //需要数据传递,用下面方法;  
            Message msg =new Message();  
            msg.obj = "数据";//可以是基本类型,可以是对象,可以是List、map等;  
            mHandler.sendMessage(msg);  

            myHandler.sendEmptyMessage(0); //其实内部实现还是和上面一样

            endEmptyMessageAtTime(int what, long uptimeMillis); //定时发送空消息
            sendEmptyMessageDelayed(int what, long delayMillis); //延时发送空消息
            sendMessageAtTime(Message msg, long uptimeMillis); //定时发送消息
            sendMessageDelayed(Message msg, long delayMillis); //延时发送消息
            sendMessageAtFrontOfQueue(Message msg); //最先处理消息(慎用)    
        }               
    }).start();  

//方法四: AsyncTask
/**
  * 该类中方法的执行顺序依次为:onPreExecute, doInBackground, onPostExecute
  注意:doInBackground方法是在子线程中,所以,我们在这个方法里面执行耗时操作。
  同时,由于其返回结果会传递到onPostExecute方法中,
  而onPostExecute方法工作在UI线程,这样我们就在这个方法里面更新ui,达到了异步更新ui的目的。
  */
private class MyAsyncTask extends AsyncTask<String, Integer, String> {
    /**
     * 主线程中执行
     * 在execute()被调用后首先执行
     * 一般用来在执行后台任务前对UI做一些标记
     */
    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        System.out.println("MyAsyncTask.onPreExecute");
    }

    /**
     * 子线程中执行,执行一些耗时操作,关键方法
     * 在执行过程中可以调用publishProgress(Progress... values)来更新进度信息。
     */
    @Override
    protected String doInBackground(String... params) {
        System.out.println("MyAsyncTask.doInBackground");
        //只是模拟了耗时操作
        int count = 0;
        for (int i = 0; i < 10; i++) {
            try {
                count++;
                publishProgress((count % 100) * 10);
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // publishProgress((int) ((count / (float) total) * 100));
        return "耗时操作执行完毕";
    }

    /**
     * 主线程中执行
     * 在调用publishProgress(Progress... values)时,此方法被执行,直接将进度信息更新到UI组件中
     */
    @Override
    protected void onProgressUpdate(Integer... values) {
        super.onProgressUpdate(values);
        progressBar.setProgress(values[0]);
        textView.setText("loading..." + values[0] + "%");
        System.out.println("MyAsyncTask.onProgressUpdate");
    }

    /**
     * 在主线程中,当后台操作结束时,此方法将会被调用
     * 计算结果将做为参数传递到此方法中,直接将结果显示到UI组件上。
     */
    @Override
    protected void onPostExecute(String aVoid) {
        super.onPostExecute(aVoid);
        System.out.println("MyAsyncTask.onPostExecute aVoid=" + aVoid);
        textView.setText(aVoid);
    }


    /**
     * 主线程中执行
     * 当异步任务取消后的,会回调该函数。在该方法内可以更新UI
     */
    @Override
    protected void onCancelled() {
        super.onCancelled();
        System.out.println("MyAsyncTask.onCancelled");
        progressBar.setProgress(0);
        textView.setText("0");
    }

    @Override
    protected void onCancelled(String s) {
        super.onCancelled(s);
    }
}

【67.重用布局 <include/> <merge/>】

注意点:
< include >

  • 重写layout_*的属性记得先重写 android:layout_height 和android:layout_width。
  • include如果指定了id,那么layout属性的根视图id会被强制修改成include中的id,如果不注意很容易出现空指针问题。

< merge >

  • 复用在LinearLayout和RelativeLayout中会有不同的表现,在前者会以线性的方式布局,后者delete按钮会遮挡add按钮,所以使用merge标签一定要注意实际的根视图类型
  • merge必须放在布局文件的根节点上

clipboard2.png

  • merge并不是一个ViewGroup,也不是一个View,它相当于声明了一些视图,等待被添加。
  • 因为merge标签并不是View,所以在通过LayoutInflate.inflate方法渲染的时候, 第二个参数必须指定一个父容器,且第三个参数必须为true,也就是必须为merge下的视图指定一个父亲节点。

clipboard3.png

  • 因为merge不是View,所以对merge标签设置的所有属性都是无效的
  • 如果Activity的布局文件根节点是FrameLayout,可以替换为merge标签,这样,执行setContentView之后,会减少一层FrameLayout节点。
  • 自定义XXXLayout控件时,如果使用LayoutInflater.inflate(R.layout.xxx, this, true)填充视图,那么该布局的根元素最好设置成,这一点其实是和上一点相同的,有助于直接减少视图层级。

< ViewStub> 懒加载View

你的布局中可能存在很少情况下才用到的复杂布局,比如单条详情、进图条或者是一些撤销消息等等,这些布局可以只在你需要的时候才加载以提升布局的渲染速度。

定义ViewStub
ViewStub 是一个轻量级的视图,它不参与绘制也不参与任何的布局工作。因此,它在视图层级的构建中消耗的资源是非常小的。每一个ViewStub在使用时只需要通过android:layout去定义它需要加载布局文件即可。

下面给出的ViewStub承载了一个透明的进度条,它只在特定情况下才需要展现给用户。

<ViewStub
    android:id="@+id/stub_import"
    android:inflatedId="@+id/panel_import"
    android:layout="@layout/progress_overlay"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="bottom" />

加载ViewStub布局
当我们需要让ViewStub承载的视图展现时,只需要通过调用setVisibility(View.VISIBLE)或者inflate()方法即可

((ViewStub) findViewById(R.id.stub_import)).setVisibility(View.VISIBLE);
// or
View importPanel = ((ViewStub) findViewById(R.id.stub_import)).inflate();

一旦ViewStub被可见或者被布局了,那么它就从视图层级中剥离出来,取代ViewStub存在于视图层级的是android:layout属性所指定的布局,该布局的id可以通过android:inflatedId指定。
这里和include一样,android:inflatedId属性也会覆盖layout中根视图的id。

注意点

  • ViewStub只能被inflate一次,多次调用会出异常。第一次setVisibility(View.Visibility)会被动调用一次inflate,因此需要注意。
  • ViewStub被inflate之后会从视图层级中移除,因此再次调用findViewById尝试获取ViewStub对象会返回空,不要尝试使用该对象,否则会出现空指针。
  • ViewStub中layout_属性都是为新加载的视图的根视图设置的,与 < include > 标签一样,ViewStub加载的根视图自身的layout_属性会被ViewStub重写。比如layout_height,它不能指定ViewStub本身的高度,因为ViewStub本身的高度和宽度都是0,它指定的其实是需要加载的布局的根视图高度。又由于此,在布局时要注意基于ViewStub的相对布局在ViewStub未inflate之前,位置与实际位置是有偏差的。
  • 一般xml文件中定义的属性都可以通过代码设置,同样ViewStub也可以通过方法setLayoutResource在代码中动态设置应该加载的layout文件,此时一个ViewStub就可以根据逻辑不同使用不同的视图。

【67.5. Parcelable和Serializable的效率对比】

Parcelable和Serializable的效率对比 Parcelable vs Serializable 号称快10倍的效率.

  • 内存序列化上选择Parcelable,
  • 存储到设备或者网络传输上选择Serializable(当然Parcelable也可以但是稍显复杂)
选择序列化方法的原则
1)在使用内存的时候,Parcelable比Serializable性能高,所以推荐使用Parcelable。
2)Serializable在序列化的时候会产生大量的临时变量,从而引起频繁的GC。
3)Parcelable不能使用在要将数据存储在磁盘上的情况,因为Parcelable不能很好的保证数据的持续性在外界有变化的情况下。尽管Serializable效率低点,但此时还是建议使用Serializable 。

链接:https://www.jianshu.com/p/df35baa91541

【待测试】

//写在android下面
applicationVariants.all { variant ->
    variant.outputs.all {
        outputFileName = "MyAppName${variant.versionName}_${releaseTime()}_${variant.name}.apk"
    }
}

//放到build里面跟apply同级就行了。
static def releaseTime() {
    SimpleDateFormat str = new SimpleDateFormat("yyyy_MM_dd_KK_mm")
    return str.format(new Date())
}
目录
相关文章
|
缓存 Java 测试技术
Android小提示六
Android一些小提示
161 0
Android小提示六
|
XML 存储 监控
Android小提示五
android开发的一些小提示
231 0
|
Java 开发工具 Android开发
Android小提示三
Android开发一些提示
112 0
Android小提示三
|
XML JavaScript Java
Android小提示二
Android开发一些提示
176 0
Android小提示二
|
XML ARouter 安全
Android小提示一
Android开发一些小提示
366 0
|
3天前
|
Linux 编译器 Android开发
FFmpeg开发笔记(九)Linux交叉编译Android的x265库
在Linux环境下,本文指导如何交叉编译x265的so库以适应Android。首先,需安装cmake和下载android-ndk-r21e。接着,下载x265源码,修改crosscompile.cmake的编译器设置。配置x265源码,使用指定的NDK路径,并在配置界面修改相关选项。随后,修改编译规则,编译并安装x265,调整pc描述文件并更新PKG_CONFIG_PATH。最后,修改FFmpeg配置脚本启用x265支持,编译安装FFmpeg,将生成的so文件导入Android工程,调整gradle配置以确保顺利运行。
24 1
FFmpeg开发笔记(九)Linux交叉编译Android的x265库
|
26天前
|
Java Android开发
Android 开发获取通知栏权限时会出现两个应用图标
Android 开发获取通知栏权限时会出现两个应用图标
12 0
|
1月前
|
设计模式 人工智能 开发工具
安卓应用开发:构建未来移动体验
【2月更文挑战第17天】 随着智能手机的普及和移动互联网技术的不断进步,安卓应用开发已成为一个热门领域。本文将深入探讨安卓平台的应用开发流程、关键技术以及未来发展趋势。通过分析安卓系统的架构、开发工具和框架,本文旨在为开发者提供全面的技术指导,帮助他们构建高效、创新的移动应用,以满足不断变化的市场需求。
18 1
|
17天前
|
XML 开发工具 Android开发
构建高效的安卓应用:使用Jetpack Compose优化UI开发
【4月更文挑战第7天】 随着Android开发不断进化,开发者面临着提高应用性能与简化UI构建流程的双重挑战。本文将探讨如何使用Jetpack Compose这一现代UI工具包来优化安卓应用的开发流程,并提升用户界面的流畅性与一致性。通过介绍Jetpack Compose的核心概念、与传统方法的区别以及实际集成步骤,我们旨在提供一种高效且可靠的解决方案,以帮助开发者构建响应迅速且用户体验优良的安卓应用。