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