This question already has answers here:
Call Activity method from adapter
(9 answers)
Closed 8 years ago.
I have a textview in a listview on clicking which it should perform some activity. Currently,I am writing onClick of textview inside getView method of custom adapter class. On click of textview , I am trigerring a method in my Activity class . But that Activity has that variable value as NULL eventhough its already been initialised in onCreate of Activity. Here's my code:
Adapter class:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
final int pos = position;
Item item = (Item) getItem(position);
TextView textView = (TextView) view
.findViewById(R.id.tv_song_title);
textView.setText(item.text);
textView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
MainActivity main = new MainActivity();
main.songPicked(pos); //calling act class method
}
});
Activity class:
private MusicService musicSrv;
public void songPicked(int position) { //method called
if (musicSrv!=null) //is null .Why??
{
musicSrv.setSong(position);
songName = musicSrv.playSong();
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
songAdt = new SongAdapter();
songAdt.setRows(rows);
songView.setAdapter(songAdt);
playMusic();
}
public void playMusic() {
if (playIntent == null) {
playIntent = new Intent(this, MusicService.class);
startService(playIntent);
bindService(playIntent, musicConnection, Context.BIND_AUTO_CREATE);
}
}
// connect to the service
private ServiceConnection musicConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
MusicBinder binder = (MusicBinder) service;
// get service
musicSrv = binder.getService();
// pass list
musicSrv.setList(songList);
musicBound = true;
Log.i("LEAKTEST", "Connected to instance " + this.toString());
}
#Override
public void onServiceDisconnected(ComponentName name) {
musicBound = false;
musicSrv = null;
}
Use listeners instead of passing the whole activity.
The main idea is
public interface SmthListener() {
void onSmthHappens(smthParams);
}
public class WhoNotify {
SmthListener mListener;
public WhoNotify(SmthListener listener) {
mListener = listener;
}
public void smthHappensInMyClass() {
mListener.onSmthHappens(smthParams);
}
}
public class WhoListene implements SmthListener {
#Override
public void onCreate(...) {
... new WhoNotify(this);
}
#Override
void onSmthHappens(smthParams) {
// do stuff;
}
}
Related
I have an app to receive data through bluetooth. The idea is that I have a bluetooth service running in the background, it is bound to the main activity. The service is created when a button is pressed on a fragment (I bind the service to the activity because I want to keep the bluetooth connection even fragment has been damaged.)
For this, When button in the fragment is pressed, I'm passing the handler from the fragment to the activity, the handler will then be passed to the service, so that I could update the fragment UI based on the received data.
However, I got non-static method cannot be reference from a static context for the getBTService method .I could not solve it as I cannot make the bindService static. Could anyone advice? Or is there a better way to manage this? Here is the relevant code:
Main Activity:
public void getBTService(Handler btHandler){
this.mHandler = btHandler;
Intent intent = new Intent(this, BluetoothService.class);
bindService(intent, connection, Context.BIND_AUTO_CREATE);
}
#Override
protected void onStop() {
super.onStop();
if (btService != null) {
unbindService(connection);
btBound = false;
}
}
private ServiceConnection connection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className,
IBinder service) {
BluetoothService.LocalBinder binder = (BluetoothService.LocalBinder) service;
btService = binder.getService(mHandler);
btBound = true;
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
btBound = false;
}
};
Fragment:
public View onCreateView(#NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_signal, container, false);
statusTV = root.findViewById(R.id.text_status);
dataTV = root.findViewById(R.id.text_data);\
openBtn = root.findViewById(R.id.openBtn);
mChart = root.findViewById(R.id.chart);
openBtn.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v) {
MainActivity.getBTService(mHandler);
}
}
);
return root;
}
private final Handler mHandler = new Handler(){
#Override
public void handleMessage(#NonNull Message msg) {
switch ((msg.what)){
case MainActivity.MessageConstants.MESSAGE_READ:
String data = (String) msg.obj;
updateTV("data", data);
}
}
};
You can try this instead of call directly by MainActivity
openBtn.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v) {
((MainActivity) requireActivity()).getBTService(mHandler);
}
}
I want to display items in list view dynamically. In my case items comes from the service. Service broadcast the item and the activity receives it and fill the array for item, that array I use to fill up the list view.
Could you please help me to know what I am doing wrong?
Here's my code:
public class MacaddressInfo extends AppCompatActivity {
ArrayList<String> listofMac=new ArrayList<String>();
ArrayAdapter<String> adapter;
ListView lv;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_macaddress_info);
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
LocalBroadcastManager.getInstance(this).registerReceiver(mMessageReceiver, new IntentFilter("intMAC"));
}
#Override
protected void onResume() {
super.onResume();
}
private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
handleMessage(intent);
}
};
private void handleMessage(Intent msg){
if(msg != null) {
Bundle data = msg.getExtras();
String res = data.getString("result");
Log.d("macaddinfo", "macaddress info is: " + res);
listofMac.add(res);
lv = (ListView)findViewById(R.id.lstMac);
adapter=new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,listofMac);
setListAdapter(adapter);
Log.i("handlemessage", "Array count is: " + listofMac.size() + " ListView count is: " + lv.getCount());
}
}
protected ListView getListView() {
if (lv == null) {
lv = (ListView) findViewById(R.id.lstMac);
}
return lv;
}
protected void setListAdapter(ListAdapter adapter) {
getListView().setAdapter(adapter);
}
}
In onhandlemessage() method, I am filling the array and setting up the adapter, but I am not sure why list view is not showing on the screen. Even I am getting the count also, "I/handlemessage: Array count is: 1 ListView count is: 1"
Below is my layout
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
tools:context="rockwell.bluetooth_pairing.MacaddressInfo"
tools:showIn="#layout/activity_macaddress_info">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="Large Text"
android:id="#+id/textView"
android:layout_alignParentTop="true"
android:layout_alignParentStart="true"
android:layout_marginTop="54dp" />
<ListView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="#+id/lstMac"
android:layout_below="#+id/textView"
android:layout_alignParentStart="true"
android:layout_marginTop="52dp"
android:choiceMode="singleChoice"/>
</RelativeLayout>
This is the code of service which broadcast the intent that will use as a values of list view
public class APIService extends Service {
private final IBinder mBinder = new LocalBinder();
public class LocalBinder extends Binder {
APIService getService() {
// Return this instance of LocalService so clients can call public methods
return APIService.this;
}
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
public void AddMacAddress(String str)
{
Log.d("APIService","message is: " + str);
Intent intent = new Intent("intMAC");
intent.putExtra("result",str);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
Log.d("APIService", "intent broadcasted");
}
}
Code to call the service
public class MainActivity extends AppCompatActivity {
ArrayList<String> listItems=new ArrayList<String>();
ArrayAdapter<String> adapter;
int clickCounter=0;
ListView lv;
EditText txtIP;
Button btnAdd,btnUpdate,btnDel;
int itemPos;
APIService mservice;
boolean mBound = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
txtIP = (EditText)findViewById(R.id.txtIP);
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
lv = (ListView)findViewById(R.id.lstItems);
adapter=new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,listItems);
setListAdapter(adapter);
lv.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
#Override
public boolean onItemLongClick(AdapterView<?> parent, View v, int position, long id) {
txtIP.setText(listItems.get(position));
itemPos = position;
Log.d("onlongclickpos", "long click pos is: " + position);
return true;
}
});
lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Intent intent = new Intent(MainActivity.this,MacaddressInfo.class);
startActivity(intent);
mservice.AddMacAddress("TestMAC");
}
});
}
#Override
protected void onStart() {
super.onStart();
// Bind to LocalService
Intent intent = new Intent(this, APIService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
#Override
protected void onStop() {
super.onStop();
// Unbind from the service
if (mBound) {
unbindService(mConnection);
mBound = false;
}
}
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className,
IBinder service) {
// We've bound to LocalService, cast the IBinder and get LocalService instance
APIService.LocalBinder binder = (APIService.LocalBinder) service;
mservice = binder.getService();
mBound = true;
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
mBound = false;
}
};
public void addItems(View v) {
txtIP = (EditText)findViewById(R.id.txtIP);
String strItem = txtIP.getText().toString();
listItems.add(strItem);
adapter.notifyDataSetChanged();
}
public void updateItem(View v) {
//txtIP = (EditText)findViewById(R.id.txtIP);
String strItem = txtIP.getText().toString();
//Toast.makeText(this,"pos is " + lv.getCheckedItemPosition(),Toast.LENGTH_LONG).show();
int pos = itemPos;
if(pos > -1) {
adapter.remove(listItems.get(pos));
adapter.insert(strItem, pos);
}
adapter.notifyDataSetChanged();
}
public void delItem(View v) {
//int pos = lv.getCheckedItemPosition();
int pos = itemPos;
if(pos>-1)
{
adapter.remove(listItems.get(pos));
}
adapter.notifyDataSetChanged();
}
protected ListView getListView() {
if (lv == null) {
lv = (ListView) findViewById(R.id.lstItems);
}
return lv;
}
protected void setListAdapter(ListAdapter adapter) {
getListView().setAdapter(adapter);
}
protected ListAdapter getListAdapter() {
ListAdapter adapter = getListView().getAdapter();
if (adapter instanceof HeaderViewListAdapter) {
return ((HeaderViewListAdapter)adapter).getWrappedAdapter();
} else {
return adapter;
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
Try assigning and initializing your views and adapters first in onCreate, then in handleMessage, you can add to the list and call adapter.notifyDataSetChanged() to tell the adapter to update the ListView.
Also, after some work, making listOfMac static ending up solving the problem. I think what was happening before in the logs was you were holding onto old instances of the MacAddressInfo class, and so it was those ListView counts that were incrementing, not the currently displayed one.
Code looks like this
public class MacAddressActivity extends AppCompatActivity {
// Static variables shared at class-level
private static final ArrayList<String> listofMac = new ArrayList<String>();
private static boolean mRegistered;
private ArrayAdapter<String> adapter;
private ListView lv;
private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
handleMessage(intent);
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_macaddress_info);
lv = (ListView) findViewById(R.id.lstMac);
adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, listofMac);
lv.setAdapter(adapter);
// fab code
}
#Override
protected void onStart() {
super.onStart();
// prevent from registering again
if (!mRegistered) {
LocalBroadcastManager.getInstance(this).registerReceiver(mMessageReceiver, new IntentFilter("intMAC"));
mRegistered = true;
}
}
#Override
protected void onPause() {
super.onPause();
// Should be unregistered, but uncommenting this makes it stop working
/*
if (mRegistered) {
LocalBroadcastManager.getInstance(this).unregisterReceiver(mMessageReceiver);
mRegistered = false;
}
*/
}
private void handleMessage(Intent msg) {
if (msg != null) {
Bundle data = msg.getExtras();
String res = data.getString("result");
Log.d("macaddinfo", "macaddress info is: " + res);
listofMac.add(res);
adapter.notifyDataSetChanged();
Log.i("handlemessage", "Array count is: " + listofMac.size() + " ListView count is: " + lv.getCount());
}
}
}
I'm writing a radio application, in which the user will be able to download some shows.
When the user click on a download button on the audio player, the show is added to the DB as a SavedShow.
The user can see the state of the SavedShow instances in a SavedShowsFragment, which shows a ListView with the list of the saved shows.
In each row of the ListView is the title of the saved show, its state (NOT_STARTED/DOWNLOADING/PAUSED/DOWNLOADED/ERROR), a ProgressBar and a Button to pause the download, resume it or play the file if it's downloaded.
Now I don't know how to bind each item of the list to my DownloadService nor how to implement listeners from DownloadService in the adapter.
Basically, I would like for each row to:
get the progress from the DownloadTask.onProgressUpdate of my DownloadService
be notified by the DownloadService when a download is finished or if there is an error
call methods from DownloadService (e.g. pause the download when clicking the button if the file is downloading)
Anybody could help?
SavedShowsFragment
public class SavedShowsFragment extends Fragment {
private DownloadService mDownloadService;
private boolean mBoundToDownloadService = false;
private ListView mListView;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.saved_show_list, container, false);
}
#Override
public void onActivityCreated (Bundle savedInstanceState){
super.onActivityCreated(savedInstanceState);
}
private void setSavedShowList(){
mListView = (ListView) getView().findViewById(R.id.listview);
SavedShowAdapter adapter = new SavedShowAdapter(getActivity(), MyApplication.dbHelper.getSavedShowList());
mListView.setAdapter(adapter);
}
#Override
public void onCreate (Bundle savedInstanceState){
super.onCreate(savedInstanceState);
// start services
Intent intent = new Intent(getActivity(), DownloadService.class);
getActivity().startService(intent);
}
#Override
public void onResume() {
super.onResume();
setSavedShowList();
// bind service
Intent intent = new Intent(getActivity(), StreamService.class);
getActivity().bindService(intent, mDownloadServiceConnection, Context.BIND_AUTO_CREATE);
}
#Override
public void onPause() {
// unbind service
if (mBoundToDownloadService) {
getActivity().unbindService(mDownloadServiceConnection);
}
super.onPause();
}
private ServiceConnection mDownloadServiceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className, IBinder service) {
/*
* Get service instance
*/
DownloadServiceBinder binder = (DownloadServiceBinder) service;
mDownloadService = binder.getService();
mBoundToDownloadService = true;
/*
* Start download for NOT_STARTED saved show
*/
for(SavedShow savedShow : MyRadioApplication.dbHelper.getSavedShowList()){
if(savedShow.getState()==SavedShow.State.NOT_STARTED){
mDownloadService.downLoadFile(savedShow.getId());
}
}
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
mBoundToDownloadService = false;
}
};
}
SavedShowAdapter
public class SavedShowAdapter extends ArrayAdapter<SavedShow> {
private LayoutInflater mLayoutInflater;
static class ViewHolder {
TextView title, status;
ProgressBar progressBar;
Button downloadStateBtn;
}
public SavedShowAdapter(Context context, List<SavedShow> saved_show_list) {
super(context, 0, saved_show_list);
mLayoutInflater = (LayoutInflater) context.getSystemService( Context.LAYOUT_INFLATER_SERVICE );
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder holder;
View v = convertView;
if (v == null) {
v = mLayoutInflater.inflate(R.layout.podcast_list_item, parent, false);
holder = new ViewHolder();
holder.title = (TextView)v.findViewById(R.id.title);
holder.status = (TextView)v.findViewById(R.id.status);
holder.progressBar = (ProgressBar)v.findViewById(R.id.progress_bar);
holder.downloadStateBtn = (Button)v.findViewById(R.id.btn_download_state);
v.setTag(holder);
} else {
holder = (ViewHolder) v.getTag();
}
holder.downloadStateBtn.setTag(position);
// TODO set button state according to item state
holder.downloadStateBtn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
int position = (Integer) v.getTag();
// TODO call DownloadService method (download or pause) according to the state
}
});
//holder.title.setText(getItem(position).getTitle());
// do a publish progress listener for progress bar
return v;
}
}
DownloadService
public class DownloadService extends Service {
private static final int MAX_DOWNLOAD_TASKS = 10;
private final IBinder mBinder = new DownloadServiceBinder();
private class MaxDownloadsException extends Exception
{
private static final long serialVersionUID = 6859017750734917253L;
public MaxDownloadsException()
{
super(getResources().getString(R.string.max_downloads));
}
}
private List<DownloadTask> mTaskList= new ArrayList<DownloadTask>();
private void addTaskToList(DownloadTask task) throws MaxDownloadsException {
if(mTaskList.size() < MAX_DOWNLOAD_TASKS+1){
mTaskList.add(task);
}
else{
throw new MaxDownloadsException();
}
}
private void removeTaskToList(DownloadTask task) {
mTaskList.remove(task);
// if no more tasks, stop service
if(mTaskList.size() == 0){
stopSelf();
}
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_NOT_STICKY;
}
/*
* Binding-related methods
*/
public class DownloadServiceBinder extends Binder {
DownloadService getService() {
return DownloadService.this;
}
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
public void downLoadFile(long savedShowId){
new DownloadTask().execute(savedShowId);
}
private class DownloadTask extends AsyncTask<Long, Integer, Void> {
private SavedShow savedShow;
private int contentLength;
#Override
protected Void doInBackground(Long... params) {
savedShow = MyApplication.dbHelper.getSavedShow(params[0]);
int contentLength;
HttpURLConnection connection = null;
BufferedInputStream in = null;
try {
URL url = new URL(savedShow.getUrl());
connection = (HttpURLConnection) url.openConnection();
long fileLength = new File(getFilesDir(), savedShow.getFilename()).length();
FileOutputStream outputStream = openFileOutput(savedShow.getFilename(), MODE_PRIVATE | MODE_APPEND);
int downloaded = 0;
contentLength = connection.getContentLength();
// If there is already a file with that filename,
// add 'Range' property in header
if(fileLength != 0){
connection.setRequestProperty("Range", "bytes=" + fileLength + "-");
}
in = new BufferedInputStream(connection.getInputStream());
//BufferedOutputStream out = new BufferedOutputStream(outputStream, 1024);
byte[] data = new byte[1024];
int x = 0;
while ((x = in.read(data, 0, 1024)) >= 0) {
outputStream.write(data, 0, x);
downloaded += x;
publishProgress((int) (downloaded));
}
} catch (IOException e) {
MyApplication.dbHelper.updateSavedShowError(savedShow.getId(), e.toString());
}
finally{
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
if(connection != null){
connection.disconnect();
}
}
return null;
}
#Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
// How can I pass values to SavedShowAdapter's items?
}
#Override
protected void onPostExecute(Void nothing) {
super.onPostExecute(nothing);
// TODO remove task
// How can I notify SavedShowAdapter's items?
}
}
}
Here's what I did:
to call DownloadService methods from the SavedShowAdapter, I passed the Service bound to the SavedShowFragment to the SavedShowAdapter.
to get DownloadService listeners in the Adapter, I set some BroadcastReceiver in the SavedShowFragment, which then call SavedShowAdapter's method that modify the data and call notifyDataSetChanged().
I see you're already binding to the service. You can inherit the default binder, and implement any methods you like on it, or create a messenger and send messages back and fourth. Both methods are explored in the API Guides, be sure to read through it and fully understand the advantages and disadvantages of each method:
http://developer.android.com/guide/components/bound-services.html
I have a trouble with getting Activity(Nullpointerexception) after that I have rotate screen and received callback from AsyncTask to update my views of the fragment. If I wont change orientation then everything is OK(but not all the time, sometimes this bug appears)
My main activity:
public class MainActivity extends SherlockFragmentActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.pager_layout);
fm = getSupportFragmentManager();
fm.addOnBackStackChangedListener(this);
session = new SessionManager(getApplicationContext());
if (session.isAuthorizated()) {
disableTabs();
FragmentTransaction ft = fm.beginTransaction();
if (session.termsAndConditions()) {
ft.replace(android.R.id.content, new TermsAndConditionsFragment(), "terms-and-conditions").commit();
}
}
} else {
enableTabs();
mTabsAdapter = new TabsAdapter(this, mViewPager);
mTabsAdapter.addTab(actionBar.newTab().setText("Log in"), LoginFragment.class, null);
mTabsAdapter.addTab(actionBar.newTab().setText("Calculator"), CalculatorFragment.class, null);
}
}
That`s my fragment:
public class TermsAndConditionsFragment extends SherlockFragment implements OnClickListener, OnTouchListener, OnEditorActionListener, ValueSelectedListener, AsyncUpdateViewsListener {
private static final String TAG = "TermsAndConditionsFragment";
private TermsAndConditionsManager termsAndConditionsM;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
prepareData();
}
public void prepareData() {
if (getSherlockActivity() == null)
Log.d(TAG, "Activity is null");
termsAndConditionsM = new TermsAndConditionsManager(getSherlockActivity().getApplicationContext());
termsAndConditions = termsAndConditionsM.getTermsAndConditions();
...
// some stuff
...
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
rootView = init(inflater, container);
return rootView;
}
private View init(LayoutInflater inflater, ViewGroup container) {
rootView = inflater.inflate(R.layout.fragment_terms_and_conditions, container, false);
//bla bla bla
return rootView;
}
public void updateTermsAndConditionsView() {
//update views here
}
#Override
public void onClick(View v) {
ft = fm.beginTransaction();
switch (v.getId()) {
case R.id.etHowMuch:
d = NumberPaymentsPickerFragment.newInstance(getSherlockActivity(), Integer.valueOf(howMuch.replace("£", "")), 0);
d.setValueSelectedListener(this);
d.show(getFragmentManager(), Const.HOW_MUCH);
break;
}
}
#Override
public void onValueSelected() {
Bundle args = new Bundle();
...
ExecuteServerTaskBackground task = new ExecuteServerTaskBackground(getSherlockActivity());
task.setAsyncUpdateViewsListener(this);
task.action = ServerAPI.GET_TERMS_AND_CONDITIONS;
task.args = args;
task.execute();
}
#Override
public void onUpdateViews() {
prepareData();
updateTermsAndConditionsView();
}
}
My AsyncTask with callback:
public class ExecuteServerTaskBackground extends AsyncTask<Void, Void, Void> {
private static final String TAG = "ExecuteServerTaskBackground";
Activity mActivity;
Context mContext;
private AsyncUpdateViewsListener callback;
public ExecuteServerTaskBackground(Activity activity) {
this.mActivity = activity;
this.mContext = activity.getApplicationContext();
}
public void setAsyncUpdateViewsListener(AsyncUpdateViewsListener listener) {
callback = listener;
}
#Override
protected Void doInBackground(Void... params) {
ServerAPI server = new ServerAPI(mContext);
if (!args.isEmpty())
msg = server.serverRequest(action, args);
else
msg = server.serverRequest(action, null);
return null;
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
callback.onUpdateViews();
}
}
Why does it behave so? How can I get activity correctly if I change orientation.
EDIT:
As I understand correctly nullpointer appears after orientation changed and asynctask executed due to wrong reference between asyctask and Activity. Recreated activity doesnt have this reference thats why when I receive callback I use wrong activity reference which isn`t exist anymore. But how can I save current activity reference?
EDIT:
I have decided to try realize my task throughout Service and that`s what I have done.
Activity:
public class MainFragment extends Fragment implements ServiceExecutorListener, OnClickListener {
private static final String TAG = MainFragment.class.getName();
Button btnSend, btnCheck;
TextView serviceStatus;
Intent intent;
Boolean bound = false;
ServiceConnection sConn;
RESTService service;
ProgressDialog pd = new ProgressDialog();
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setRetainInstance(true);
intent = new Intent(getActivity(), RESTService.class);
getActivity().startService(intent);
sConn = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder binder) {
Log.d(TAG, "MainFragment onServiceConnected");
service = ((RESTService.MyBinder) binder).getService();
service.registerListener(MainFragment.this);
if (service.taskIsDone())
serviceStatus.setText(service.getResult());
bound = true;
}
public void onServiceDisconnected(ComponentName name) {
Log.d(TAG, "MainFragment onServiceDisconnected");
bound = false;
}
};
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.main_fragment, container, false);
serviceStatus = (TextView) rootView.findViewById(R.id.tvServiceStatusValue);
btnSend = (Button) rootView.findViewById(R.id.btnSend);
btnCheck = (Button) rootView.findViewById(R.id.btnCheck);
btnSend.setOnClickListener(this);
btnCheck.setOnClickListener(this);
return rootView;
}
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btnSend:
pd.show(getFragmentManager(), "ProgressDialog");
service.run(7);
service.run(2);
service.run(4);
break;
case R.id.btnCheck:
if (service != null)
serviceStatus.setText(String.valueOf(service.taskIsDone()) + service.getTasksCount());
break;
}
}
#Override
public void onStart() {
super.onStart();
Log.d(TAG, "Bind service");
getActivity().bindService(intent, sConn, 0);
}
#Override
public void onPause() {
super.onDestroy();
Log.d(TAG, "onDestroy: Unbind service");
if (!bound)
return;
getActivity().unbindService(sConn);
service.unregisterListener(this);
bound = false;
}
#Override
public void onComplete(String result) {
Log.d(TAG, "Task Completed");
pd.dismiss();
serviceStatus.setText(result);
}
}
Dialog:
public class ProgressDialog extends DialogFragment implements OnClickListener {
final String TAG = ProgressDialog.class.getName();
public Dialog onCreateDialog(Bundle savedInstanceState) {
setRetainInstance(true);
AlertDialog.Builder adb = new AlertDialog.Builder(getActivity())
.setTitle("Title!")
.setPositiveButton(R.string.yes, this)
.setNegativeButton(R.string.no, this)
.setNeutralButton(R.string.maybe, this)
.setCancelable(false)
.setMessage(R.string.message_text)
.setOnKeyListener(new OnKeyListener() {
#Override
public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
return true;
}
});
return adb.create();
}
public void onClick(DialogInterface dialog, int which) {
int i = 0;
switch (which) {
case Dialog.BUTTON_POSITIVE:
i = R.string.yes;
break;
case Dialog.BUTTON_NEGATIVE:
i = R.string.no;
break;
case Dialog.BUTTON_NEUTRAL:
i = R.string.maybe;
break;
}
if (i > 0)
Log.d(TAG, "Dialog 2: " + getResources().getString(i));
}
public void onDismiss(DialogInterface dialog) {
Log.d(TAG, "Dialog 2: onDismiss");
// Fix to avoid simple dialog dismiss in orientation change
if ((getDialog() != null) && getRetainInstance())
getDialog().setDismissMessage(null);
super.onDestroyView();
}
public void onCancel(DialogInterface dialog) {
super.onCancel(dialog);
Log.d(TAG, "Dialog 2: onCancel");
}
}
Service:
public class RESTService extends Service {
final String TAG = RESTService.class.getName();
MyBinder binder = new MyBinder();
ArrayList<ServiceExecutorListener> listeners = new ArrayList<ServiceExecutorListener>();
Handler h = new Handler();
RequestManager mRequest;
ExecutorService es;
Object obj;
int time;
StringBuilder builder;
String result = null;
public void onCreate() {
super.onCreate();
Log.d(TAG, "RESTService onCreate");
es = Executors.newFixedThreadPool(1);
obj = new Object();
builder = new StringBuilder();
}
public void run(int time) {
RunRequest rr = new RunRequest(time);
es.execute(rr);
}
class RunRequest implements Runnable {
int time;
public RunRequest(int time) {
this.time = time;
Log.d(TAG, "RunRequest create");
}
public void run() {
Log.d(TAG, "RunRequest start, time = " + time);
try {
TimeUnit.SECONDS.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
Log.d(TAG, "RunRequest obj = " + obj.getClass());
} catch (NullPointerException e) {
Log.d(TAG, "RunRequest error, null pointer");
}
builder.append("result " + time + ", ");
result = builder.toString();
sendCallback();
}
}
private void sendCallback() {
h.post(new Runnable() {
#Override
public void run() {
for (ServiceExecutorListener listener : listeners)
listener.onComplete();
}
});
}
public boolean taskIsDone() {
if (result != null)
return true;
return false;
}
public String getResult() {
return result;
}
public void registerListener(ServiceExecutorListener listener) {
listeners.add(listener);
}
public void unregisterListener(ServiceExecutorListener listener) {
listeners.remove(listener);
}
public IBinder onBind(Intent intent) {
Log.d(TAG, "RESTService onBind");
return binder;
}
public boolean onUnbind(Intent intent) {
Log.d(TAG, "RESTService onUnbind");
return true;
}
public class MyBinder extends Binder {
public RESTService getService() {
return RESTService.this;
}
}
}
As you mention in your edit, the current Activity is destroyed and recreated on orientation change.
But how can I save current activity reference?
You shouldn't. The previous Activity is no longer valid. This will not only cause NPEs but also memory leaks because the AsyncTask might hold the reference to old Activity, maybe forever.
Solution is to use Loaders.
I am doing some investigations with the GoogleTV and a Android tablet. I have managed to make an android application that can send control messages to the google tv from the main Activity, what I am trying to do is launch a new activity from the main Activity and continue using the AnymoteClientService Service with the new activity. In my main activity I get an anymoteSender handle which I use to send KeyEvent messages to the google tv, how do I transfer this to the new activity (SlidepuzzleActivity)? I could instantiate it all again, but that would mean having to go through the whole pairing process again.
From the code below you will see that I have an anymoteSender in my SlidepuzzleActivity class, this will throw an error, but illustrates where I need to reuse that variable.
Code:
MainActivity.java:
package uk.co.myapp.gtvremote;
//imports removed for paste
public class MainActivity extends Activity implements ClientListener{
private AnymoteSender anymoteSender;
private TextView statusText;
protected AnymoteClientService mAnymoteClientService;
private static String statusPrefix = "Status: ";
private Context mContext;
private ProgressBar progressBar;
private Handler handler;
private TouchHandler touchPadHandler;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
progressBar = (ProgressBar) findViewById(R.id.a_progressbar);
progressBar.setVisibility(View.VISIBLE);
mContext = this;
ImageButton upArrowButton = (ImageButton) findViewById(R.id.upArrow);
ImageButton leftArrowButton = (ImageButton) findViewById(R.id.leftArrow);
ImageButton centreButton = (ImageButton) findViewById(R.id.centreButton);
ImageButton rightArrowButton = (ImageButton) findViewById(R.id.rightArrow);
ImageButton downArrowButton = (ImageButton) findViewById(R.id.downArrow);
ImageButton backButton = (ImageButton) findViewById(R.id.backButton);
ImageButton homeButton = (ImageButton) findViewById(R.id.homeButton);
Button testButton = (Button) findViewById(R.id.testButton);
upArrowButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
sendKeyEvent(KeyEvent.KEYCODE_DPAD_UP);
}
});
leftArrowButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
sendKeyEvent(KeyEvent.KEYCODE_DPAD_LEFT);
}
});
centreButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
sendKeyEvent(KeyEvent.KEYCODE_DPAD_CENTER);
}
});
rightArrowButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
sendKeyEvent(KeyEvent.KEYCODE_DPAD_RIGHT);
}
});
downArrowButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
sendKeyEvent(KeyEvent.KEYCODE_DPAD_DOWN);
}
});
backButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
sendKeyEvent(KeyEvent.KEYCODE_BACK);
}
});
homeButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
sendKeyEvent(KeyEvent.KEYCODE_HOME);
}
});
testButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Intent myIntent = new Intent(MainActivity.this, SlidepuzzleActivity.class);
MainActivity.this.startActivity(myIntent);
}
});
handler = new Handler();
// Bind to the AnymoteClientService
Intent intent = new Intent(mContext, AnymoteClientService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
statusText = (TextView) findViewById(R.id.statusText);
}
/** Defines callbacks for service binding, passed to bindService() */
private ServiceConnection mConnection = new ServiceConnection() {
/*
* ServiceConnection listener methods.
*/
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
mAnymoteClientService = ((AnymoteClientService.AnymoteClientServiceBinder) service)
.getService();
mAnymoteClientService.attachClientListener(MainActivity.this);
}
#Override
public void onServiceDisconnected(ComponentName name) {
mAnymoteClientService.detachClientListener(MainActivity.this);
mAnymoteClientService = null;
}
};
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
#Override
public void onConnected(AnymoteSender anymoteSender) {
if (anymoteSender != null) {
// Send events to Google TV using anymoteSender.
// save handle to the anymoteSender instance.
this.anymoteSender = anymoteSender;
attachTouchListnertoTouchPad();
} else {
statusText.setText(statusPrefix + "Connection attempt failed, cant find send handler");
//attempt to connect again?
//attemptToConnect();
}
// Hide the progressBar once connection to Google TV is established. Make the text display appropriately
handler.post(new Runnable() {
public void run() {
progressBar.setVisibility(View.INVISIBLE);
statusText.setText(statusPrefix + "Connected to GoogleTV");
}
});
}
#Override
public void onDisconnected() {
// show message to tell the user about disconnection.
statusText.setText(statusPrefix + "Disconnected");
// Try to connect again if needed. This may be need to be done via button
attemptToConnect();
this.anymoteSender = null;
}
#Override
public void onConnectionError() {
// show message to tell the user about disconnection.
statusText.setText(statusPrefix + "Connection error encountered");
// Try to connect again if needed.
attemptToConnect();
this.anymoteSender = null;
}
#Override
protected void onDestroy() {
if (mAnymoteClientService != null) {
mAnymoteClientService.detachClientListener(this);
}
unbindService(mConnection);
super.onDestroy();
}
private void attachTouchListnertoTouchPad()
{
// Attach touch handler to the touchpad view
touchPadHandler = new TouchHandler(
findViewById(R.id.touchPad), Mode.POINTER_MULTITOUCH, anymoteSender);
}
public void attemptToConnect()
{
//stub to invoke connection attempt
}
private void sendKeyEvent(final int keyEvent) {
// create new Thread to avoid network operations on UI Thread
if (anymoteSender == null) {
Toast.makeText(MainActivity.this, "Waiting for connection",
Toast.LENGTH_LONG).show();
return;
}
anymoteSender.sendKeyPress(keyEvent);
}
}
SlidepuzzleActivity.java:
package uk.co.myapp.gtvremote;
//imports removed for paste
public class SlidepuzzleActivity extends Activity implements ClientListener{
private AnymoteClientService mAnymoteClientService;
private Context mContext;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.slidepuzzle);
mContext = this;
ImageButton piece1x1 = (ImageButton) findViewById(R.id.piece1x1);
ImageButton piece1x2 = (ImageButton) findViewById(R.id.piece1x2);
ImageButton piece1x3 = (ImageButton) findViewById(R.id.piece1x3);
ImageButton piece2x1 = (ImageButton) findViewById(R.id.piece2x1);
ImageButton piece2x2 = (ImageButton) findViewById(R.id.piece2x2);
ImageButton piece2x3 = (ImageButton) findViewById(R.id.piece2x3);
ImageButton piece3x1 = (ImageButton) findViewById(R.id.piece3x1);
ImageButton piece3x2 = (ImageButton) findViewById(R.id.piece3x2);
ImageButton piece3x3 = (ImageButton) findViewById(R.id.piece3x3);
Intent intent = new Intent(mContext, AnymoteClientService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
piece1x1.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
}
});
}
private ServiceConnection mConnection = new ServiceConnection() {
/*
* ServiceConnection listener methods.
*/
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
mAnymoteClientService = ((AnymoteClientService.AnymoteClientServiceBinder) service)
.getService();
mAnymoteClientService.attachClientListener(SlidepuzzleActivity.this);
}
#Override
public void onServiceDisconnected(ComponentName name) {
mAnymoteClientService.detachClientListener(SlidepuzzleActivity.this);
mAnymoteClientService = null;
}
};
private void sendKeyEvent(final int keyEvent) {
// create new Thread to avoid network operations on UI Thread
if (anymoteSender == null) {
Toast.makeText(SlidepuzzleActivity.this, "Waiting for connection",
Toast.LENGTH_LONG).show();
return;
}
anymoteSender.sendKeyPress(keyEvent);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.slidepuzzle, menu);
return true;
}
#Override
public void onConnected(AnymoteSender anymoteSender) {
// TODO Auto-generated method stub
}
#Override
public void onDisconnected() {
// TODO Auto-generated method stub
}
#Override
public void onConnectionError() {
// TODO Auto-generated method stub
}
#Override
protected void onDestroy() {
if (mAnymoteClientService != null) {
mAnymoteClientService.detachClientListener(this);
}
unbindService(mConnection);
super.onDestroy();
}
}
Just published an update to AnymoteLibrary for this.
In your MainActivity call both bindService() (already) and startService() for AnymoteClientService. The reason behind calling startService() is to keep the service and its anymoteSender instance around so that other Activitys in the same app can use it.
In the second Activity, implement ClientListener ( if you want to get onDisconnected() callback) and bind to the service and attachClientListener(). To get the AnymoteSender instance, call AnymoteClientService.getAnymoteSender(). Note that it can return null if the connection to Google TV is lost.
When all Activitys are done using AnymoteSender, remember to call stopService() for AnymoteClientService.