最近文章

Perl解决Can't locate xxx.pm in @INC

在安装一些perl程序时,有时会报错如下:Can't locate xxx.pm in @INC这个原因时没有找到perl的模块。只需要安装即可。使用cpan安装perl模块。1、启动cpan# cpan输入安装模块install Chocolate::Belgian也可以使用cpan直接安装cpan Chocolate::Belgian
标签:

解决Android Studio 3.5自动格式化XML后导致view的顺序改变

升级到Android Studio 3.5后,对xml做自动格式化后,xml里的元素位置的顺序被改变了,特别是view元素,这样会导致界面错乱。找到的解决方案是需要配置xml布局。步骤如下:一、Mac上打开Android Studio的喜好配置Android Studio > Preferences. 而对于Windows则是, File > Settings
标签:

Android指定SnackBar在屏幕的位置

Snackbar 常以一个小的弹出框的形式,出现在手机屏幕下方或者桌面左下方,并且是在屏幕所有层的最上方。如果要指定它在屏幕出现的位置,可以把SnackBar放置在android.support.design.widget.CoordinatorLayout内。在RelativeLayout里添加CoordinatorLayout如下:<android.support.design.widg
标签:

Android使用DiffUtil智能地更新RecyclerView

DiffUtil是一个工具类,它会找出两个列表的差异,输出一个更新列表,并且可以用来通知RecyclerView的Adapter更新。使用DiffUtil会使用DiffUtil.Callback来计算两个列表的差异,其中DiffUtil.Callback是一个抽象类,需要我们实现它,它包含了四个抽象方法和一个非抽象方法:getOldListSize():获取旧列表的大小getNewListSize
标签:

使用Robolectric写Android单元测试

Robolectric是一个单元测试框架,运行在jvm上。相对于在Android模拟器或设备上运行测试需要花费一分甚至更长时间,Robolectric只需要几秒钟。这里简单介绍下使用Robolectric做单元测试。添加依赖首先在build.gradle添加Robolectric依赖,并设置android的测试选项unitTests.includeAndroidResources为truetest
标签:

RecyclerView添加点击事件监听器

实现RecyclerView.OnItemTouchListener的项目触摸监听器:import android.content.Context; import android.support.v7.widget.RecyclerView; import android.view.GestureDetector; import android.view.M
标签:

处理guava-21.0不兼容Android错误:DexArchiveBuilderException: Failed to process guava-21.0.jar

在Android项目里使用guava-21.0报错:Error:com.android.builder.dexing.DexArchiveBuilderException: Failed to process C:\Users\cc\.gradle\caches\modules-2\files-2.1\com.google.guava\guava\21.0\
标签:

Android安装错误App not installed原因分析

App not installed可能原因:App与Android设备或版本不兼容使用了不同的证书签名不同版本的apk,这会导致重新安装失败。app的签名不正确,检查下是否与选择的Signature Version相关v2为Android 7新增的签名方式。可以参考APK signature scheme v2
标签:

Android Studio 3 AndroidManifest.xml报错,error: unknown element &lt;action&gt; found.

Android Studio 3默认启动了AAPT2,AAPT2增强了对AndroidManifest.xml的检查。先前版本的AAPT会忽略AndroidManifest.xml错误的元素内嵌,或者只是给出警告,并不会报错。示例<manifest xmlns:android="http://schemas.android.com/apk/res/an
标签:

安装Android SDK Platform Tools:Download finished with wrong checksum

安装Android SDK Platform Tools,总是报校验和错误:Done loading packages. Preparing to install archives Downloading Android SDK Platform-tools, revision&
标签:

Android Studio 3 Gradle插件报错:AAPT2 error: check logs for details

升级到Android Studio3.0 RC 2,使用gradle报错信息如下: Error:failed linking file resources. Error:java.util.concurrent.ExecutionException:  java.util.concurrent.ExecutionExceptio
标签:

Android dex文件反编译为Java源码

工具准备 dex2jar:国人写的一个dex反编译为java的工具jd-gui:查看java源码的gui工具反编译步骤步骤一把test_apk-debug.apk里的classes.dex转换为test_apk-debug_dex2jar.jard2j-dex2jar.sh -f -o output_jar.jar apk_to_decompile.apk
标签:

Perl解决Can't locate xxx.pm in @INC

在安装一些perl程序时,有时会报错如下:

Can't locate xxx.pm in @INC

这个原因时没有找到perl的模块。只需要安装即可。

使用cpan安装perl模块。

1、启动cpan

# cpan

输入安装模块

install Chocolate::Belgian

也可以使用cpan直接安装

cpan Chocolate::Belgian


解决Android Studio 3.5自动格式化XML后导致view的顺序改变


升级到Android Studio 3.5后,对xml做自动格式化后,xml里的元素位置的顺序被改变了,特别是view元素,这样会导致界面错乱。

找到的解决方案是需要配置xml布局。步骤如下:

一、Mac上打开Android Studio的喜好配置Android Studio > Preferences. 而对于Windows则是, File > Settings

二、在配置的界面搜索xml

三、选择Code Style下的xml

四、选择右边界面的右上角有一个Set from的选项,点击,并且选择predefineed style为Android



Android指定SnackBar在屏幕的位置

Snackbar 常以一个小的弹出框的形式,出现在手机屏幕下方或者桌面左下方,并且是在屏幕所有层的最上方。如果要指定它在屏幕出现的位置,可以把SnackBar放置在android.support.design.widget.CoordinatorLayout内。

在RelativeLayout里添加CoordinatorLayout如下:

<android.support.design.widget.CoordinatorLayout
    android:layout_width="match_parent"
    android:layout_height="200dp"
    android:id="@+id/myCoordinatorLayout"
    android:layout_alignParentTop="true"
    android:layout_alignParentLeft="true"
    android:layout_alignParentStart="true">
</android.support.design.widget.CoordinatorLayout>

调用Snackbar.make()把CoordinatorLayout作为第一个参数,如下:

final View viewPos = findViewById(R.id.myCoordinatorLayout);    
Snackbar.make(viewPos, R.string.snackbar_text, Snackbar.LENGTH_LONG)
                            .setAction(R.string.snackbar_action_undo, showListener)
                            .show();

可以通过改变CoordinatorLayout的位置来改变Snackbar的位置,如设置android:paddingBottom="16dp"。

Android使用DiffUtil智能地更新RecyclerView

DiffUtil是一个工具类,它会找出两个列表的差异,输出一个更新列表,并且可以用来通知RecyclerView的Adapter更新。

使用

DiffUtil会使用DiffUtil.Callback来计算两个列表的差异,其中DiffUtil.Callback是一个抽象类,需要我们实现它,它包含了四个抽象方法和一个非抽象方法:

  • getOldListSize():获取旧列表的大小
  • getNewListSize():获取新列表的大小
  • areItemsTheSame(int oldItemPosition, int newItemPosition):判断新旧列表两个位置的项目是否相同
  • areContentsTheSame(int oldItemPosition, int newItemPosition):判断新旧列表两个位置的项目数据是否相同,DiffUtil只会在areItemsTheSame()返回true时才调用此方法。
  • getChangePayload(int oldItemPosition, int newItemPosition):如果areItemTheSame()返回true,而areContentsTheSame()返回false,则DiffUtil会调用此方法来获取更改后的负载

示例

模型类:Employee

public class Employee {
    public int id;
    public String name;
    public String role;
}

实现DiffUtil.Callback

public class EmployeeDiffCallback extends DiffUtil.Callback {

    private final List<Employee> mOldEmployeeList;
    private final List<Employee> mNewEmployeeList;

    public EmployeeDiffCallback(List<Employee> oldEmployeeList, List<Employee> newEmployeeList) {
        this.mOldEmployeeList = oldEmployeeList;
        this.mNewEmployeeList = newEmployeeList;
    }

    @Override
    public int getOldListSize() {
        return mOldEmployeeList.size();
    }

    @Override
    public int getNewListSize() {
        return mNewEmployeeList.size();
    }

    @Override
    public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
        return mOldEmployeeList.get(oldItemPosition).getId() == mNewEmployeeList.get(
                newItemPosition).getId();
    }

    @Override
    public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
        final Employee oldEmployee = mOldEmployeeList.get(oldItemPosition);
        final Employee newEmployee = mNewEmployeeList.get(newItemPosition);

        return oldEmployee.getName().equals(newEmployee.getName());
    }

    @Nullable
    @Override
    public Object getChangePayload(int oldItemPosition, int newItemPosition) {
        // 如果要使用ItemAnimator,可以在实现此方法
        return super.getChangePayload(oldItemPosition, newItemPosition);
    }
}

在RecyclerViewAdapter处理列表数据的更新

public class CustomRecyclerViewAdapter extends RecyclerView.Adapter<CustomRecyclerViewAdapter.ViewHolder> {

  ...
       public void updateEmployeeListItems(List<Employee> employees) {
        final EmployeeDiffCallback diffCallback = new EmployeeDiffCallback(this.mEmployees, employees);
        final DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(diffCallback);

        this.mEmployees.clear();
        this.mEmployees.addAll(employees);
        diffResult.dispatchUpdatesTo(this); //分发更新到RecyclerView Adapter
    }
}

DiffResult为计算差异后返回的结果,获取差异结果后,调用dispatchUpdatesTo(RecyclerView.Adapter)来分发更新的列表。 

DiffUtil调用了RecyclerView.Adapter的以下方法来通知Adapter来更新数据集:

  • notifyItemMoved()
  • notifyItemRangeChanged()
  • notifyItemRangeInserted()
  • notifyItemRangeRemoved()

使用Robolectric写Android单元测试

Robolectric是一个单元测试框架,运行在jvm上。相对于在Android模拟器或设备上运行测试需要花费一分甚至更长时间,Robolectric只需要几秒钟。

这里简单介绍下使用Robolectric做单元测试。

添加依赖

首先在build.gradle添加Robolectric依赖,并设置android的测试选项unitTests.includeAndroidResources为true

testImplementation  "org.robolectric:robolectric:3.6.1"

android {
  testOptions {
    unitTests {
      includeAndroidResources = true
    }
  }
}

对于Mac和Linux用户,需要配置Android JUnit的工作目录为$MODULE_DIRS:

Run -> Edit Configurations -> Defaults -> Android Junit -> Working Directory填入$MODULE_DIRS

写测试用例

新建DemoTest

@RunWith(RobolectricTestRunner.class)
@Config(constants = BuildConfig.class)
public class DemoTest {
    // 测试代码
}

在DemoTest添加注解:

  • @RunWith,指定测试的Runner为RobolectricTestRunner.class
  • @Config,指定配置的类为BuildConfig

在开始实际的测试之前,可以在@Before的方法上构建activity

@Before
public void setUp() throws Exception {
    activity = Robolectric.buildActivity(FirstActivity.class)
            .create()
            .resume()
            .get();
}

这一步是可选的,在开始测试前构建activity,是为了后续能够在此Activity上执行操作。

测试用例

@Test
public void launchSecondActivity() {
    Intent expectedIntent = new Intent(activity, SecondActivity.class);
    //模拟点击按钮
    activity.findViewById(R.id.launch_second_activity_button).callOnClick();

    //使用Shadows获取实际启动的Activity
    ShadowActivity shadowActivity = Shadows.shadowOf(activity);
    Intent actualIntent = shadowActivity.getNextStartedActivity();

    //检测expectedIntent是否和actualIntent匹配
    assertTrue(expectedIntent.filterEquals(actualIntent));
}

在此测试用例里模拟了按钮的点击事件,检测启动的Activity是否和预期启动的Activity匹配。

接着是运行测试用例:右键选中launchSecondActivity测试方法,选择“Run launchSecondActivity”

RecyclerView添加点击事件监听器

实现RecyclerView.OnItemTouchListener的项目触摸监听器:

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;


public class RecyclerItemClickListener implements RecyclerView.OnItemTouchListener {
  private OnItemClickListener mListener;

  public interface OnItemClickListener {
    public void onItemClick(View view, int position);

    public void onLongItemClick(View view, int position);
  }

  GestureDetector mGestureDetector;

  public RecyclerItemClickListener(Context context, final RecyclerView recyclerView, OnItemClickListener listener) {
    mListener = listener;
    mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
        @Override
        public boolean onSingleTapUp(MotionEvent e) {
            return true;
        }

        @Override
        public void onLongPress(MotionEvent e) {
            View child = recyclerView.findChildViewUnder(e.getX(), e.getY());
            if (child != null && mListener != null) {
                mListener.onLongItemClick(child, recyclerView.getChildAdapterPosition(child));
            }
        }
    });
}

  @Override public boolean onInterceptTouchEvent(RecyclerView view, MotionEvent e) {
    View childView = view.findChildViewUnder(e.getX(), e.getY());
    if (childView != null && mListener != null && mGestureDetector.onTouchEvent(e)) {
      mListener.onItemClick(childView, view.getChildAdapterPosition(childView));
      return true;
    }
    return false;
  }

  @Override public void onTouchEvent(RecyclerView view, MotionEvent motionEvent) { }

  @Override
  public void onRequestDisallowInterceptTouchEvent (boolean disallowIntercept){}
}

OnItemClickListener 接口提供了两个方法:

  • onItemClick:监听点击事件
  • onLongItemClick:监听长按事件,其中长按时间使用GestureDetector来监听

点击事件可以在这两个方法里做处理。

使用

RecyclerView recyclerView = findViewById(R.id.recycler);
recyclerView.addOnItemTouchListener(
    new RecyclerItemClickListener(context, recyclerView ,new RecyclerItemClickListener.OnItemClickListener() {
      @Override public void onItemClick(View view, int position) {
        // 点击事件处理
      }

      @Override public void onLongItemClick(View view, int position) {
        // 长按事件处理
      }
    })
);

处理guava-21.0不兼容Android错误:DexArchiveBuilderException: Failed to process guava-21.0.jar

在Android项目里使用guava-21.0报错:

Error:com.android.builder.dexing.DexArchiveBuilderException: Failed to process C:\Users\cc\.gradle\caches\modules-2\files-2.1\com.google.guava\guava\21.0\3a3d111be1be1b745edfa7d91678a12d7ed38709\guava-21.0.jar
Error:com.android.builder.dexing.DexArchiveBuilderException: Error while dexing.
Error:com.android.tools.r8.ApiLevelException: Default interface methods are only supported starting with Android N (--min-api 24): java.util.Collection com.google.common.collect.BiMap.values()
Error:Execution failed for task ':app:transformClassesWithDexBuilderForDebug'.

原因是guava 21使用的是Java 8,与Android不兼容。

可以把guava 21改为与Android兼容的版本23.3-android

dependencies {
  compile 'com.google.guava:guava:23.3-android'
}

Android安装错误App not installed原因分析

App not installed可能原因:

  • App与Android设备或版本不兼容
  • 使用了不同的证书签名不同版本的apk,这会导致重新安装失败。
  • app的签名不正确,检查下是否与选择的Signature Version相关

v2为Android 7新增的签名方式。可以参考APK signature scheme v2

Android Studio 3 AndroidManifest.xml报错,error: unknown element <action> found.

Android Studio 3默认启动了AAPT2,AAPT2增强了对AndroidManifest.xml的检查。

先前版本的AAPT会忽略AndroidManifest.xml错误的元素内嵌,或者只是给出警告,并不会报错。

示例

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
   package="com.example.myname.myapplication">
   <application
       ...
       <activity android:name=".MainActivity">
           <intent-filter>
               <action android:name="android.intent.action.MAIN" />
               <category android:name="android.intent.category.LAUNCHER" />
           </intent-filter>
           <action android:name="android.intent.action.CUSTOM" />
       </activity>
   </application>
</manifest>

在AAPT2对于放错位置的标签会抛出错误,上面的例子会抛出类似错误:

AndroidManifest.xml:15: error: unknown element <action> found.

解决方法就是根据manifest的结构定位错误的标签或放错位置的标签。

Manifest结构参考:https://developer.android.com/guide/topics/manifest/manifest-intro.html#filestruct

安装Android SDK Platform Tools:Download finished with wrong checksum

安装Android SDK Platform Tools,总是报校验和错误:

Done loading packages.
Preparing to install archives
Downloading Android SDK Platform-tools, revision 26.0.2
Download interrupted: Connection to https://dl.google.com refused
Done. Nothing was installed.
Preparing to install archives
Downloading Android SDK Platform-tools, revision 26.0.2
Download finished with wrong checksum. Expected 98832431e339c82be1fe910a733a3782071200fd, got 1be4d6d486ff4b8f2edcdf801d27a6ce5c1eba8c.
Done. Nothing was installed.

解决方法:

为了避免在校验和出错,可以自己下载解压安装。

  1. 下载:SDK Platform-Tools
  2. 解压到android的SDK的根目录,我的为C:\Users\username\AppData\Local\Android\Sdk
  3. 新的目录为C:\Users\username\AppData\Local\Android\Sdk\platform-tools
  4. 运行SDK Manager.exe

Android Studio 3 Gradle插件报错:AAPT2 error: check logs for details

升级到Android Studio3.0 RC 2,使用gradle报错信息如下:


Error:failed linking file resources.
Error:java.util.concurrent.ExecutionException: 
java.util.concurrent.ExecutionException: 
com.android.tools.aapt2.Aapt2Exception: AAPT2 error: check logs for details
Error:java.util.concurrent.ExecutionException: 
com.android.tools.aapt2.Aapt2Exception: AAPT2 error: check logs for details
Error:com.android.tools.aapt2.Aapt2Exception: AAPT2 error: check logs for 
details
Error:Execution failed for task ':app:processDebugResources'.
> Failed to execute aapt
Information:BUILD FAILED in 27s
Information:11 errors
Information:0 warnings

解决方法

Android的Gradle 3.0插件默认启动Aapt2,目的是为了改进增量资源的处理。

如果有问题可以退回到之前的版本,打开gradle.properties,添加如下内容

android.enableAapt2=false

参考:https://developer.android.com/studio/build/gradle-plugin-3-0-0.html

Android dex文件反编译为Java源码

更新于 2017.10.05 1分钟阅读 0 评论 5 推荐

    Android

    作者: yheng
  1. Android dex文件反编译为Java源码 Page 1

工具准备

  1.  dex2jar:国人写的一个dex反编译为java的工具
  2. jd-gui:查看java源码的gui工具

反编译步骤

步骤一

把test_apk-debug.apk里的classes.dex转换为test_apk-debug_dex2jar.jar

d2j-dex2jar.sh -f -o output_jar.jar apk_to_decompile.apk

在window下,d2j-dex2jar.sh 改为 d2j-dex2jar.bat。

参考:dex2jar documentation

步骤二

用jd-gui打开反编译后的jar包