android 面试 高级 android面试2020_android-studio

前言

写在前面:首先是不一次性放出来的原因:资料来之不易,希望大家好好珍惜,每天花一段时间细细的消化这些题目,其次希望大家在阅读题目的时候最好跟着书或者代码一起阅读、一起敲,做到熟稔于心,信手拈来,这样面试的时候才能展现你最自信的一面。

101、Android 5.0-将页眉/页脚添加到RecyclerView

答案:

我必须在其中添加页脚RecyclerView,在这里我分享了我的代码段,因为我认为它可能有用。请检查代码中的注释,以更好地了解整体流程。

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import java.util.ArrayList;

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

    private static final int FOOTER_VIEW = 1;
    private ArrayList<String> data; // Take any list that matches your requirement.
    private Context context;

    // Define a constructor
    public RecyclerViewWithFooterAdapter(Context context, ArrayList<String> data) {
        this.context = context;
        this.data = data;
    }

    // Define a ViewHolder for Footer view
    public class FooterViewHolder extends ViewHolder {
        public FooterViewHolder(View itemView) {
            super(itemView);
            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    // Do whatever you want on clicking the item
                }
            });
        }
    }

    // Now define the ViewHolder for Normal list item
    public class NormalViewHolder extends ViewHolder {
        public NormalViewHolder(View itemView) {
            super(itemView);

            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    // Do whatever you want on clicking the normal items
                }
            });
        }
    }

    // And now in onCreateViewHolder you have to pass the correct view
    // while populating the list item.

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

        View v;

        if (viewType == FOOTER_VIEW) {
            v = LayoutInflater.from(context).inflate(R.layout.list_item_footer, parent, false);
            FooterViewHolder vh = new FooterViewHolder(v);
            return vh;
        }

        v = LayoutInflater.from(context).inflate(R.layout.list_item_normal, parent, false);

        NormalViewHolder vh = new NormalViewHolder(v);

        return vh;
    }

    // Now bind the ViewHolder in onBindViewHolder
    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {

        try {
            if (holder instanceof NormalViewHolder) {
                NormalViewHolder vh = (NormalViewHolder) holder;

                vh.bindView(position);
            } else if (holder instanceof FooterViewHolder) {
                FooterViewHolder vh = (FooterViewHolder) holder;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // Now the critical part. You have return the exact item count of your list
    // I've only one footer. So I returned data.size() + 1
    // If you've multiple headers and footers, you've to return total count
    // like, headers.size() + data.size() + footers.size()

    @Override
    public int getItemCount() {
        if (data == null) {
            return 0;
        }

        if (data.size() == 0) {
            //Return 1 here to show nothing
            return 1;
        }

        // Add extra view to show the footer view
        return data.size() + 1;
    }

    // Now define getItemViewType of your own.

    @Override
    public int getItemViewType(int position) {
        if (position == data.size()) {
            // This is where we'll add footer.
            return FOOTER_VIEW;
        }

        return super.getItemViewType(position);
    }

    // So you're done with adding a footer and its action on onClick.
    // Now set the default ViewHolder for NormalViewHolder

    public class ViewHolder extends RecyclerView.ViewHolder {
        // Define elements of a row here
        public ViewHolder(View itemView) {
            super(itemView);
            // Find view by ID and initialize here
        }

        public void bindView(int position) {
            // bindView() method to implement actions
        }
    }
}

上面的代码段在中添加了页脚RecyclerView。你可以检查此GitHub存储库,以检查添加页眉和页脚的实现。

102、如何使Android设备振动?

答案:

尝试:

import android.os.Vibrator;
...
Vibrator v = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
// Vibrate for 500 milliseconds
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    v.vibrate(VibrationEffect.createOneShot(500, VibrationEffect.DEFAULT_AMPLITUDE));
} else {
    //deprecated in API 26 
    v.vibrate(500);
}

注意:

不要忘记在AndroidManifest.xml文件中包含权限:

<uses-permission android:name="android.permission.VIBRATE"/>

103、Android / Java中的JSON数组迭代

答案:

我已经用两种不同的方式做到了

1.) make a Map

HashMap<String, String> applicationSettings = new HashMap<String,String>();
        for(int i=0; i<settings.length(); i++){
            String value = settings.getJSONObject(i).getString("value");
            String name = settings.getJSONObject(i).getString("name");
            applicationSettings.put(name, value);
        }

2.) make a JSONArray of names

JSONArray names = json.names();
    JSONArray values = json.toJSONArray(names);
    for(int i=0; i<values.length(); i++){
        if (names.getString(i).equals("description")){
            setDescription(values.getString(i));
        }
        else if (names.getString(i).equals("expiryDate")){
            String dateString = values.getString(i);
            setExpiryDate(stringToDateHelper(dateString)); 
        }
        else if (names.getString(i).equals("id")){
            setId(values.getLong(i));
        }
        else if (names.getString(i).equals("offerCode")){
            setOfferCode(values.getString(i));
        }
        else if (names.getString(i).equals("startDate")){
            String dateString = values.getString(i);
            setStartDate(stringToDateHelper(dateString));
        }
        else if (names.getString(i).equals("title")){
            setTitle(values.getString(i));
        }
    }

104、this.getClass()。getClassLoader()。getResource(“…”)和NullPointerException

答案:

使用时

this.getClass().getResource("myFile.ext")

getResource将尝试查找相对于包的资源。如果您使用:

this.getClass().getResource("/myFile.ext")

getResource 会将其视为绝对路径,并像完成操作一样简单地调用类加载器。

this.getClass().getClassLoader().getResource("myFile.ext")

之所以不能/ClassLoader路径中使用前导,是因为所有ClassLoader路径都是绝对的,因此路径中的/第一个字符不是有效的。

105、数字证书:如何使用.cer文件将.cer文件导入?

答案:

# Copy the certificate into the directory Java_home\Jre\Lib\Security
# Change your directory to Java_home\Jre\Lib\Security>
# Import the certificate to a trust store.

keytool -import -alias ca -file somecert.cer -keystore cacerts -storepass changeit [Return]

Trust this certificate: [Yes]

changeit是默认的信任库密码

106、如何以编程方式清除应用程序数据

答案:

您可以使用包管理器工具清除已安装应用的数据(类似于按设备上应用设置中的“清除数据”按钮)。因此,使用adb可以做到:

adb shell pm clear my.wonderful.app.package

107、如何修复’android.os.NetworkOnMainThreadException’?

答案:

当应用程序尝试在其主线程上执行联网操作时,将引发此异常。在AsyncTask以下位置运行代码:

class RetrieveFeedTask extends AsyncTask<String, Void, RSSFeed> {

    private Exception exception;

    protected RSSFeed doInBackground(String... urls) {
        try {
            URL url = new URL(urls[0]);
            SAXParserFactory factory = SAXParserFactory.newInstance();
            SAXParser parser = factory.newSAXParser();
            XMLReader xmlreader = parser.getXMLReader();
            RssHandler theRSSHandler = new RssHandler();
            xmlreader.setContentHandler(theRSSHandler);
            InputSource is = new InputSource(url.openStream());
            xmlreader.parse(is);

            return theRSSHandler.getFeed();
        } catch (Exception e) {
            this.exception = e;

            return null;
        } finally {
            is.close();
        }
    }

    protected void onPostExecute(RSSFeed feed) {
        // TODO: check this.exception
        // TODO: do something with the feed
    }
}

如何执行任务:

在MainActivity.java文件中,您可以在oncreate()方法中添加此行

new RetrieveFeedTask().execute(urlToRssFeed);

不要忘记将其添加到AndroidManifest.xml文件中:

<uses-permission android:name="android.permission.INTERNET"/>

108、在Android手机上检查方向

答案:

用于确定要检索哪些资源的当前配置可从资源的Configuration对象中获得:

getResources().getConfiguration().orientation;

你可以通过查看方向值来检查方向:

int orientation = getResources().getConfiguration().orientation;
if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
    // In landscape
} else {
    // In portrait
}

109、如何从Android Studio项目制作.jar文件

答案:

  • 打开库项目的build.gradle
  • 在build.gradle中编写两个任务-deleteJar和createJar并添加规则createJar.dependsOn(deleteJar,build)

上面的代码:

task deleteJar(type: Delete) {
    delete 'libs/jars/logmanagementlib.jar'
}           

task createJar(type: Copy) {
    from('build/intermediates/bundles/release/')
    into('libs/jars/')
    include('classes.jar')
    rename('classes.jar', 'logmanagementlib.jar')
}

createJar.dependsOn(deleteJar, build)
  • 从右侧展开gradle面板,然后在“ library”->“ others”下打开所有任务。你将在此处看到两个新任务-createJar和deleteJar
  • 双击createJar
  • 任务成功运行后,从createJar任务中提到的路径(即libs / xxxx.jar)获取生成的jar。 在此处
  • 将新生成的jar复制到所需项目的lib文件夹中->右键单击->选择“添加为库”

110、无法启动Eclipse-Java已启动,但返回了退出代码= 13

答案:

有OS,JDK和Eclipse bitity的有效组合。就我而言,我在64位OS上使用64位JDK和32位Eclipse。将JDK降级为32位后,Eclipse开始工作。

请使用以下组合之一。

  • 32位OS,32位JDK,32位Eclipse(仅32位)
  • 64位OS,32位JDK,32位Eclipse
  • 64位OS,64位JDK,64位Eclipse(仅64位)

111、在运行时确定Android视图的大小

答案:

在视图上使用ViewTreeObserver等待第一个布局。只有在第一个布局之后,getWidth()/ getHeight()/ getMeasuredWidth()/ getMeasuredHeight()才能工作。

ViewTreeObserver viewTreeObserver = view.getViewTreeObserver();
if (viewTreeObserver.isAlive()) {
  viewTreeObserver.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
    @Override
    public void onGlobalLayout() {
      view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
      viewWidth = view.getWidth();
      viewHeight = view.getHeight();
    }
  });
}

112、由于AsyncTask是一个单独的类,如何将OnPostExecute()的结果获取到主要活动中?

答案:

简单:

创建interface类,其中class String output是可选的,或者可以是您想要返回的任何变量。

public interface AsyncResponse {
    void processFinish(String output);
}

转到您的AsyncTask课程,并将interface声明AsyncResponse为字段:

public class MyAsyncTask extends AsyncTask<Void, Void, String> {
  public AsyncResponse delegate = null;

    @Override
    protected void onPostExecute(String result) {
      delegate.processFinish(result);
    }
 }

在您的主要活动中,需要进行implements交互AsyncResponse。

public class MainActivity implements AsyncResponse{
  MyAsyncTask asyncTask =new MyAsyncTask();

  @Override
  public void onCreate(Bundle savedInstanceState) {

     //this to set delegate/listener back to this class
     asyncTask.delegate = this;

     //execute the async task 
     asyncTask.execute();
  }

  //this override the implemented method from asyncTask
  @Override
  void processFinish(String output){
     //Here you will receive the result fired from async class 
     //of onPostExecute(result) method.
   }
 }

更新

我不知道这是你们中许多人的最爱。因此,这是使用简单便捷的方式interface

仍然使用相同的interface。仅供参考,您可以将其合并为AsyncTask类。

AsyncTask课堂上:

public class MyAsyncTask extends AsyncTask<Void, Void, String> {

  // you may separate this or combined to caller class.
  public interface AsyncResponse {
        void processFinish(String output);
  }

  public AsyncResponse delegate = null;

    public MyAsyncTask(AsyncResponse delegate){
        this.delegate = delegate;
    }

    @Override
    protected void onPostExecute(String result) {
      delegate.processFinish(result);
    }
}

在你的Activity课上做

public class MainActivity extends Activity {

   MyAsyncTask asyncTask = new MyAsyncTask(new AsyncResponse(){

     @Override
     void processFinish(String output){
     //Here you will receive the result fired from async class 
     //of onPostExecute(result) method.
     }
  }).execute();

 }

或者,再次在Activity上实现接口

public class MainActivity extends Activity 
    implements AsyncResponse{

    @Override
    public void onCreate(Bundle savedInstanceState) {

        //execute the async task 
        new MyAsyncTask(this).execute();
    }

    //this override the implemented method from AsyncResponse
    @Override
    void processFinish(String output){
        //Here you will receive the result fired from async class 
        //of onPostExecute(result) method.
    }
}

如您所见,上面有2个解决方案,第一个和第三个,它需要创建method processFinish,另一个,该方法在调用者参数内部。第三个更整洁,因为没有嵌套的匿名类。希望这可以帮助

提示:变化String outputString response以及String result不同的匹配类型,以获得不同的对象。

113、如何在Android中从文件读取/写入字符串

答案:

希望这对你有用。

写文件:

private void writeToFile(String data,Context context) {
    try {
        OutputStreamWriter outputStreamWriter = new OutputStreamWriter(context.openFileOutput("config.txt", Context.MODE_PRIVATE));
        outputStreamWriter.write(data);
        outputStreamWriter.close();
    }
    catch (IOException e) {
        Log.e("Exception", "File write failed: " + e.toString());
    } 
}

读取文件:

private String readFromFile(Context context) {

    String ret = "";

    try {
        InputStream inputStream = context.openFileInput("config.txt");

        if ( inputStream != null ) {
            InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
            String receiveString = "";
            StringBuilder stringBuilder = new StringBuilder();

            while ( (receiveString = bufferedReader.readLine()) != null ) {
                stringBuilder.append("\n").append(receiveString);
            }

            inputStream.close();
            ret = stringBuilder.toString();
        }
    }
    catch (FileNotFoundException e) {
        Log.e("login activity", "File not found: " + e.toString());
    } catch (IOException e) {
        Log.e("login activity", "Can not read file: " + e.toString());
    }

    return ret;
}

114、如何正确将片段的实例状态保存在后堆栈中?

答案:

要正确保存实例状态,Fragment请执行以下操作:

1.在片段中,通过覆盖保存实例状态onSaveInstanceState()并在中还原onActivityCreated()

class MyFragment extends Fragment {

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        ...
        if (savedInstanceState != null) {
            //Restore the fragment's state here
        }
    }
    ...
    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);

        //Save the fragment's state here
    }

}

2.而且很重要的一点,在活动中,你必须保存片段的实例在onSaveInstanceState()和恢复onCreate()

class MyActivity extends Activity {

    private MyFragment 

    public void onCreate(Bundle savedInstanceState) {
        ...
        if (savedInstanceState != null) {
            //Restore the fragment's instance
            mMyFragment = getSupportFragmentManager().getFragment(savedInstanceState, "myFragmentName");
            ...
        }
        ...
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);

        //Save the fragment's instance
        getSupportFragmentManager().putFragment(outState, "myFragmentName", mMyFragment);
    }

}

希望这可以帮助。

115、如何使用抛出的参数记录日志?

答案:

有没有办法把两者String并Object[]java.util.logging.Logger中。

我会转换String[]String并使用:

public void log(Level level,
                String msg,
                Throwable thrown)

您还可以创建log指向的自己的方法,java.util.logging.Logger.log例如:

public void log(Level level,
                    String msg,
                    Object[] obj,
                    Throwable thrown)
{
   //StringBuilder buff = ...
   // some string manipulation with 'msg' and 'obj'
   // ...
   log(level, buff.toString(), thrown);
}

总体看来,JUL正在向迈进log(Level, Throwable, () -> String.format(msg, params))。但是,这不使用日志记录参数数组,该数组具有一些带有过滤器的实用程序。

另一种选择是创建一个辅助方法来构造日志记录,并让调用者对其进行记录:

public class ThrownWithParams {

    private static final Logger logger = Logger.getLogger("test");

    public static final void main(String[] args) {
        logger.log(of(logger, Level.SEVERE, new Exception("Fake"),
                "Test {0}, {1}", "FOO", "BAR"));
    }

    private static LogRecord of(Logger l, Level v, Throwable t, String m, Object... p) {
        //Let the caller invoke get/setSourceClassName or get/setSourceMethodName.
        LogRecord r = new LogRecord(v, m);
        r.setLoggerName(l.getName());
        r.setResourceBundleName(l.getResourceBundleName());
        r.setResourceBundle(l.getResourceBundle());
        r.setParameters(p);
        r.setThrown(t);
        return r;
    }
}

这是理想的,因为调用者记录了该记录,这意味着推断出的源方法和源类名称将是正确的。

116、Listview显示错误的图像

答案:

  • 问题来自于您的convertView:它是整个列表中使用的同一实例,因此,当异步加载完成时,当listview尝试使用相同的convertView绘制其他项目时,图像会更改(或在这种情况下) ,其子imageView)。
painting position 1, uses placeholder, starts loading image 1 asynchronously
painting position 2, uses placeholder, starts loading image 2 asynchronously
image 1 loading is complete, calling setImageBitmap on imageView
painting position 3, uses image 1, starts loading image 3 asynchronously
etc.

但是,您可以做的是在listadapter中保留位图缓存。像这样:

private Bitmap[] bitmapList;
private Bitmap bitmapPlaceholder;  

private void initBitmapListWithPlaceholders(){ 
// call this whenever the list size changes
// you can also use a list or a map or whatever so you 
// don't need to drop all the previously loaded bitmap whenever 
// the list contents are modified
    int count = getListCount();
    bitmapList = new Bitmap[count];
    for(int i=0;i<count;i++){
         bitmapList[i]=bitmapPlaceholder
    }
}

private void onBitmapLoaded(int position, Bitmap bmp){
// this is your callback when the load async is done
    bitmapList[position] = bmp;
}

public View getView(int position, View convertView, ViewGroup parent) {
    View v = convertView;
    ImageView imageView;
    TextView textView;
    if (v == null) {
        LayoutInflater vi = (LayoutInflater) getContext().getSystemService(
                Context.LAYOUT_INFLATER_SERVICE);
        v = vi.inflate(R.layout.drink_list_row, null);
    }

    Drink drink = allItems.get(position);
    if (drink != null && v != null) {
        imageView = (ImageView) v.findViewById(R.id.picture);
        textView = (TextView) v.findViewById(R.id.drinkName);
        imageView.setVisibility(View.GONE);
        imageView.setImageBitmap(bitmapList[position]);
        loadImageBitmap(drink, position); // this should call onBitmapLoaded(int position, Bitmap bmp) when finished to update the bitmapList and replace the placeholder
        textView.setText(drink.getName());

        if (subItems != null && subItems.contains(drink)) {
            textView.setVisibility(View.VISIBLE);
            imageView.setVisibility(View.VISIBLE);
        } else {
            textView.setVisibility(View.GONE);
            imageView.setVisibility(View.GONE);
        }
    }
    return v;
}

117、C#-如何检测Windows关闭/注销并取消该操作(询问用户之后)

答案:

有一个称为的静态类SystemEvents,它公开了这种行为:

http://msdn.microsoft.com/zh-CN/library/microsoft.win32.systemevents.aspx

但是,它无法区分某些操作,也不会暂停OS进程超时防护。我使用了一次,但是注册表中配置的默认超时时间有点短,因此可能需要增加。

简而言之,这一切都让人觉得有些骇人听闻。

118、如何使用意图将对象从一个Android活动发送到另一个?

答案:

如果您只是传递对象,那么Parcelable就是为此而设计的。与使用Java的本机序列化相比,使用它需要付出更多的努力,但是它的速度更快(我的意思是,WAY更快)。

在文档中,有关如何实现的一个简单示例是:

// simple class that just has one member property as an example
public class MyParcelable implements Parcelable {
    private int mData;

    /* everything below here is for implementing Parcelable */

    // 99.9% of the time you can just ignore this
    @Override
    public int describeContents() {
        return 0;
    }

    // write your object's data to the passed-in Parcel
    @Override
    public void writeToParcel(Parcel out, int flags) {
        out.writeInt(mData);
    }

    // this is used to regenerate your object. All Parcelables must have a CREATOR that implements these two methods
    public static final Parcelable.Creator<MyParcelable> CREATOR = new Parcelable.Creator<MyParcelable>() {
        public MyParcelable createFromParcel(Parcel in) {
            return new MyParcelable(in);
        }

        public MyParcelable[] newArray(int size) {
            return new MyParcelable[size];
        }
    };

    // example constructor that takes a Parcel and gives you an object populated with it's values
    private MyParcelable(Parcel in) {
        mData = in.readInt();
    }
}

请注意,如果要从给定的宗地中检索多个字段,则必须按照将其放入的相同顺序(即,采用FIFO方法)进行操作。

一旦实现Parcelable了对象,只需使用putExtra()将它们放入您的Intent中即可:

Intent i = new Intent();
i.putExtra("name_of_extra", myParcelableObject);

然后,您可以使用getParcelableExtra()将它们拉出:

Intent i = getIntent();
MyParcelable myParcelableObject = (MyParcelable) i.getParcelableExtra("name_of_extra");
如果您的对象类实现了Parcelable和Serializable,则请确保将其强制转换为以下之一:

i.putExtra("parcelable_extra", (Parcelable) myParcelableObject);
i.putExtra("serializable_extra", (Serializable) myParcelableObject);

119、在Android上解析查询字符串

答案:

由于Android M,事情变得更加复杂。android.net.URI .getQueryParameter()的答案有一个错误,该错误在JellyBean之前打乱了空格。 阿帕奇URLEncodedUtils.parse()工作,但以L弃用,并以M删除。

因此,最好的答案是UrlQuerySanitizer。自API级别1起就存在,并且仍然存在。它还使你考虑棘手的问题,例如如何处理特殊字符或重复值。

最简单的代码是

UrlQuerySanitizer.ValueSanitizer sanitizer = UrlQuerySanitizer.getAllButNullLegal();
// remember to decide if you want the first or last parameter with the same name
// If you want the first call setPreferFirstRepeatedParameter(true);
sanitizer.parseUrl(url);
String value = sanitizer.getValue("paramname"); // get your value

120、如何将位图缓存到本机内存中

答案:

说明示例代码显示了如何存储2个不同的位图(较小的位图,但这只是一个演示),回收原始的Java位图,然后将它们还原为Java实例并使用它们。

您可能会猜到,该布局有2个imageViews。我没有在代码中包含它,因为它很明显。

请记住,如果需要,可以将代码更改为您自己的软件包,否则将无法正常工作。

MainActivity.java-如何使用:

package com.example.jnibitmapstoragetest;
...
public class MainActivity extends Activity
  {
  @Override
  protected void onCreate(final Bundle savedInstanceState)
    {
    super.onCreate(savedInstanceState);
    //
    Bitmap bitmap=BitmapFactory.decodeResource(getResources(),R.drawable.ic_launcher);
    final JniBitmapHolder bitmapHolder=new JniBitmapHolder(bitmap);
    bitmap.recycle();
    //
    Bitmap bitmap2=BitmapFactory.decodeResource(getResources(),android.R.drawable.sym_action_call);
    final JniBitmapHolder bitmapHolder2=new JniBitmapHolder(bitmap2);
    bitmap2.recycle();
    //
    setContentView(R.layout.activity_main);
      {
      bitmap=bitmapHolder.getBitmapAndFree();
      final ImageView imageView=(ImageView)findViewById(R.id.imageView1);
      imageView.setImageBitmap(bitmap);
      }
      {
      bitmap2=bitmapHolder2.getBitmapAndFree();
      final ImageView imageView=(ImageView)findViewById(R.id.imageView2);
      imageView.setImageBitmap(bitmap2);
      }
    }
  }

JniBitmapHolder.java-JNI和JAVA之间的“桥梁”:

package com.example.jnibitmapstoragetest;
...
public class JniBitmapHolder
  {
  ByteBuffer _handler =null;
  static
    {
    System.loadLibrary("JniBitmapStorageTest");
    }

  private native ByteBuffer jniStoreBitmapData(Bitmap bitmap);

  private native Bitmap jniGetBitmapFromStoredBitmapData(ByteBuffer handler);

  private native void jniFreeBitmapData(ByteBuffer handler);

  public JniBitmapHolder()
    {}

  public JniBitmapHolder(final Bitmap bitmap)
    {
    storeBitmap(bitmap);
    }

  public void storeBitmap(final Bitmap bitmap)
    {
    if(_handler!=null)
      freeBitmap();
    _handler=jniStoreBitmapData(bitmap);
    }

  public Bitmap getBitmap()
    {
    if(_handler==null)
      return null;
    return jniGetBitmapFromStoredBitmapData(_handler);
    }

  public Bitmap getBitmapAndFree()
    {
    final Bitmap bitmap=getBitmap();
    freeBitmap();
    return bitmap;
    }

  public void freeBitmap()
    {
    if(_handler==null)
      return;
    jniFreeBitmapData(_handler);
    _handler=null;
    }

  @Override
  protected void finalize() throws Throwable
    {
    super.finalize();
    if(_handler==null)
      return;
    Log.w("DEBUG","JNI bitmap wasn't freed nicely.please rememeber to free the bitmap as soon as you can");
    freeBitmap();
    }
  }

Android.mk-JNI的属性文件:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := JniBitmapStorageTest
LOCAL_SRC_FILES := JniBitmapStorageTest.cpp
LOCAL_LDLIBS := -llog
LOCAL_LDFLAGS += -ljnigraphics

include $(BUILD_SHARED_LIBRARY)
APP_OPTIM := debug
LOCAL_CFLAGS := -g

JniBitmapStorageTest.cpp-“神奇”的东西在这里:

#include <jni.h>
#include <jni.h>
#include <android/log.h>
#include <stdio.h>
#include <android/bitmap.h>
#include <cstring>
#include <unistd.h>

#define  LOG_TAG    "DEBUG"
#define  LOGD(...)  __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)
#define  LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)

extern "C"
  {
  JNIEXPORT jobject JNICALL Java_com_example_jnibitmapstoragetest_JniBitmapHolder_jniStoreBitmapData(JNIEnv * env, jobject obj, jobject bitmap);
  JNIEXPORT jobject JNICALL Java_com_example_jnibitmapstoragetest_JniBitmapHolder_jniGetBitmapFromStoredBitmapData(JNIEnv * env, jobject obj, jobject handle);
  JNIEXPORT void JNICALL Java_com_example_jnibitmapstoragetest_JniBitmapHolder_jniFreeBitmapData(JNIEnv * env, jobject obj, jobject handle);
  }

class JniBitmap
  {
  public:
    uint32_t* _storedBitmapPixels;
    AndroidBitmapInfo _bitmapInfo;
    JniBitmap()
      {
      _storedBitmapPixels = NULL;
      }
  };

JNIEXPORT void JNICALL Java_com_example_jnibitmapstoragetest_JniBitmapHolder_jniFreeBitmapData(JNIEnv * env, jobject obj, jobject handle)
  {
  JniBitmap* jniBitmap = (JniBitmap*) env->GetDirectBufferAddress(handle);
  if (jniBitmap->_storedBitmapPixels == NULL)