error=86, Bad CPU type in executable

最近在维护一个`N`久的项目时,发现在`mac`升级系统为`10.15.5`后(Android Studio 4.0,gradle 2.3.1),编译失败了,报错如下:

Cannot run program "/Users/xxxx/Android/sdk/build-tools/23.0.1/aapt": error=86, Bad CPU type in executable

原因是最新版本的`macOS Catalina(10.15.5)`已经不支持`32`位的应用了,只能运行`64`位的应用。

解决方法:升级工程的`buildToolsVersion`,本例中将`23.0.1` 升级成`25.0.3`。

参考链接


error=86, Bad CPU type in executable

Android如何实现exclude aar包中的某个jar包

要移除的jar包不在aar包中的classes.jar中

直接把aar包里的Jar打包的时候给去掉,就像下面这这样。注意,要使用exclude module这种方式,直接使用exclude group方式没有效果。exclude group的方法适用于exclude JAR包中的文件。

implementation(name: '×××××××aar', ext: 'aar') {
    exclude module: 'gson'
}

但是,上面的操作有时候不能生效,尤其是复杂依赖的情况下。我们可能需要在每个间接依赖上都手工进行排除操作,这样就非常麻烦了。

这时候最简单的办法就是移除不需要的jar包,然后重新打包了。

或者要移除的jar包在aar包中的classes.jar中

这个时候,使用exclude方法已经不能生效了。你可以使用下面的通用方法

解压aar文件到tmpDir目录下

$ unzip ×××.aar -d tmpDir

找到classes.jar包,用压缩工具打开,删除目标文件

将tmpDir重新打包成一个新的aar

$ jar cvf ×××NewLib.aar -C tmpDir/ .

另外,有人开发了 排除AAR(Jar)包中冗余或者冲突类的gradle脚本 。如果图方便的话,可以直接用这个脚本来排除。

参考链接


Android ViewPager存在界面卡住的BUG

最近开发的项目出现界面莫名其秒的卡住,直到发生`ANR`异常退出的问题。
问题排查许久,才发现是由于使用的`ViewPager`导致的。
在`ViewPager`中使用`setCurrentItem`长距离设置当前显示的位置的时候,比如`0->600`,`600->1000`,`1000->500` 这样的长距离跳转。会导致函数卡住在

void populate(int newCurrentItem)

函数中很长时间, 甚至一直不能跳出循环。具体卡住的的循环代码如下:

for(int pos = this.mCurItem + 1; pos < N; ++pos) { 
    if (extraWidthRight >= rightWidthNeeded && pos > endPos) { 
        if (ii == null) {
            break;
        }

        if (pos == ii.position && !ii.scrolling) {
            this.mItems.remove(itemIndex);
            this.mAdapter.destroyItem(this, pos, ii.object);
            ii = itemIndex < this.mItems.size() ? (ViewPager.ItemInfo)this.mItems.get(itemIndex) : null;
        }
    } else if (ii != null && pos == ii.position) {
        extraWidthRight += ii.widthFactor;
        ++itemIndex;
        ii = itemIndex < this.mItems.size() ? (ViewPager.ItemInfo)this.mItems.get(itemIndex) : null;
    } else {
        ii = this.addNewItem(pos, itemIndex);
        ++itemIndex;
        extraWidthRight += ii.widthFactor;
        ii = itemIndex < this.mItems.size() ? (ViewPager.ItemInfo)this.mItems.get(itemIndex) : null;
    }
}

上述代码中循环体会一直循环到`N`结束为止。而这个`N`是通过

int N = this.mAdapter.getCount();

赋值的。恰好,我们的代码中返回的是`Integer.MAX_VALUE`。咨询了同事,当时设置这个数值的目的是为了解决循环滚动展示才设置的。代码是直接抄的 Android无限广告轮播 自定义BannerView / 如何优雅的实现一个可复用的 PagerAdapter

通过Google搜索关键词 `BannerAdapter extends PagerAdapter` 可以找到类似的代码。

具体的复现`BUG`的例子代码如下:

package com.mobibrw.viewpager;

import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;

import java.util.Random;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        final ViewPager viewPager = findViewById(R.id.viewPager);
        viewPager.setAdapter(new PagerAdapter() {
            @Override
            public int getCount() {
                return Integer.MAX_VALUE;
            }

            @Override
            public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
                return view == object;
            }

            @Override
            public void destroyItem(ViewGroup container, int position, Object object) {
                container.removeView((View) object);
            }

            @Override
            public Object instantiateItem(ViewGroup container, final int position) {
                final View view = LayoutInflater.from(MainActivity.this).inflate(R.layout.pager_view, null);
                final TextView tv = view.findViewById(R.id.text);
                tv.setText("" + position);
                container.addView(view);
                return view;
            }
        });

        final Button btnCmd = findViewById(R.id.btnCmd);
        btnCmd.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Random rdm = new Random(System.currentTimeMillis());
                int rd = Math.abs(rdm.nextInt())%700 + 1;
                viewPager.setCurrentItem(rd);
            }
        });
    }
}
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
    <android.support.v4.view.ViewPager
        android:id="@+id/viewPager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:clipChildren="false"
        android:clipToPadding="false"/>
    <Button
        android:id="@+id/btnCmd"
        android:text="@string/change_size"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        />

</android.support.constraint.ConstraintLayout>
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/hello_world"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.53" />

</android.support.constraint.ConstraintLayout>

完整的项目代码可以点击此处下载 ViewPager / ViewPagerAppCompatV7。项目编译完成之后,多点击几次 `Change Size` 按钮就可以复现界面长时间卡住的情况。第二个项目是支持`appcompat-v7:18.0.0`的版本,目的是观察各个版本的代码是否存在不同,结论是,都一样

这个问题目前的解决方法就是保证`ViewPager`中的数据不要太多,比如底层分页显示,每页不要太多。另外就是不要大距离跳转,否则会有很多问题

如果能把`android.support`升级到`androidx`的话,可以试试最新的`ViewPager2`,最新的`ViewPager2`是用`RecyclerView `实现的,应该不会有以前的问题了。

这个问题,我已经向Google提交了BUG,估计最后的答复也是要升级到`androidx`。

参考链接


Android无限广告轮播 - ViewPager源码分析