I make a game about 8Puzzle on android with AI using A* algorithm.
Everything works fine but there is a problem, there are some function that is executed in parallel.
What I want is the function is executed after another function is finished.
Here is code:
if(AItype.equals("A*"))
{
DisableButton();
DisableClickImageView();
AStarSolver as = new AStarSolver();
as.solvePuzzle(arr, GOAL); //Solve the puzzle
displayResult(as.solutionPath); //display animation
as = null;
Toast.makeText(getApplicationContext(), "Finished", Toast.LENGTH_LONG).show();
copySTARTtoArray();
setImageResource();
EnableButton();
}
I want text "Finished" is displayed after function displayResult() is finished but the text "Finished"
show at the same time with function displayResult().
How to solve this??
Edit:
Here is the code for displayResult();
public void displayResult(final Stack<Node> solutionPath)
{
Handler handler = new Handler();
for (int i = 0; i < solutionPath.size(); i++)
{
handler.postDelayed(new Runnable() {
public void run() {
Node solNode = solutionPath.pop();
//solNode.NodeState.
tile00.setImageResource(solNode.getImageResourceBasedNodeState(0));
tile01.setImageResource(solNode.getImageResourceBasedNodeState(1));
tile02.setImageResource(solNode.getImageResourceBasedNodeState(2));
tile10.setImageResource(solNode.getImageResourceBasedNodeState(3));
tile11.setImageResource(solNode.getImageResourceBasedNodeState(4));
tile12.setImageResource(solNode.getImageResourceBasedNodeState(5));
tile20.setImageResource(solNode.getImageResourceBasedNodeState(6));
tile21.setImageResource(solNode.getImageResourceBasedNodeState(7));
tile22.setImageResource(solNode.getImageResourceBasedNodeState(8));
}
}, 800 * (i + 1));
}
}
It display the result of animation (Ex: tile0 to tile1)
Try this.
public void displayResult(final Stack<Node> solutionPath)
{
Handler handler = new Handler();
for (int i = 0; i < solutionPath.size(); i++)
{
handler.postDelayed(new Runnable() {
public void run() {
Node solNode = solutionPath.pop();
//solNode.NodeState.
tile00.setImageResource(solNode.getImageResourceBasedNodeState(0));
.
.
.
tile22.setImageResource(solNode.getImageResourceBasedNodeState(8));
}
}, 800 * (i + 1));
}
// Put following code
handler.postDelayed(
new Runnable() {
public void run() {
Toast.makeText(getApplicationContext(), "Finished", Toast.LENGTH_LONG).show();
}
},800*(solutionPath.size()+1)
);
}
Your code should be look like this.
solvePuzzle(...){
// solved #1
displayResult(...){
}
// solved #2
displayResult(...){
}
}
or use thread.
as.solvePuzzle(arr, GOAL); //Solve the puzzle
displayResult(as.solutionPath); //display animation
means solvePuzzle is all done, then call displayResult.
function(){
//code of this function
function1();
}
function1(){
//code of this function
function2();
}
function2(){
}
Related
I use Looper.prepare and Looper.loop in Runnable's run function. But the problem is that the thread not loop at all, the Runnable just run one time. In Activity1, I use three Runnable threads, all looping. Two threads get Data and pictures from net constantly through "while" loop(needn't update UI), one thread select data and pic from local sqlite constantly through "Looper". The data is:
protected void onCreate(Bundle savedInstanceState) {
......
new Thread(getMessageTask).start();
getMessageHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
i++;
System.out.println("niuanmata" + i); //one appear the first one
try {
ArrayList<Map<String, String>> listMessages = (ArrayList<Map<String, String>>)msg.obj;
boolean listchange = true;
if (oldMessages.size() != 0) {
if (listMessages.size() == oldMessages.size()) {
for (int i = 0; i < listMessages.size(); i++) {
Map<String, String> oldmessage = (Map<String, String>) oldMessages.get(i);
Map<String, String> newmessage = (Map<String, String>) listMessages.get(i);
if ((oldmessage.get("mID") != newmessage.get("mID")) || (oldmessage.get("mainContent") != newmessage.get("mainContent")) || (oldmessage.get("deadLine") != newmessage.get("deadLine"))) {
break;
}
if (i == (listMessages.size() - 1)) {
listchange = false;
}
}
}
}
if (listchange) {
SimpleAdapter adapter = new SimpleAdapter(MainActivity.this, listMessages, R.layout.layout_invites,
new String[]{"mID", "creater", "mainContent", "deadLine", "mtype", "createrLogo"},
new int[]{R.id.tv_list_type, R.id.tv_list_name, R.id.tv_list_inviteword, R.id.tv_list_invitedate, R.id.tv_list_inviteid, R.id.iv_list_logo});
lvMessage.setAdapter(adapter);
oldMessages = listMessages;
}
} catch (Exception e) {
Toast.makeText(MainActivity.this, "wrong: " + e.toString(), Toast.LENGTH_SHORT).show();
return;
}
}
};
......
lvMessage.setOnItemClickListener(new AdapterView.OnItemClickListener(){
#Override
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
long arg3) { //when creater click, update the message; when others click, reset the alarm
Toast.makeText(MainActivity.this, "ok" , Toast.LENGTH_SHORT).show();
}
});
}
.........
Runnable synchroDataTask = new Runnable() {
#Override
public synchronized void run() {
//data syschno
while (IOHelper.loopjudge()) {
{
AccountsDB adb = new AccountsDB(MainActivity.this);
String thelastupdate = adb.getLastUpdate(account.getChatNO());
Calendar calendar = IOHelper.StringToCalendar(thelastupdate);
calendar.add(Calendar.MINUTE, -30);
String accountData = synchroDataWebservice(account.getChatNO(), IOHelper.CalendarToString(calendar)); //get the datas of the account synchroly
AccountBLL.saveDBofWebString(accountData, MainActivity.this, account); //use static method to save the DB string as SQLite data
}
}
.........
#Override
public synchronized void run() {
while (IOHelper.loopjudge()) {
......
}
.......
Runnable getMessageTask = new Runnable() {
#Override
public synchronized void run() {
Looper.prepare();
//while (IOHelper.loopjudge() && (!stopThread)) {
MessageDB messagedb = new MessageDB(MainActivity.this);
List<MessageMain> messages = messagedb.getMessageByChatNO(account.getChatNO());
ArrayList<Map<String, String>> listMessages = setMessaageListToMap(messages);
Message msg = Message.obtain();
msg.obj = listMessages;
getMessageHandler.sendMessageDelayed(msg, 1000);
//}
Looper.loop();
}
};
......
In my limited experience with android, I use while to do the Loop in getMessageTask , because the data and UI's listview need to be updated constantly. But the listview can not be clicked. Then change to Looper, but the the UI's listview can't be updated constantly....
The answer is that I misunderstand the meaning of Looper, think the Looper.prepare() and Looper.loop() as the while() loop, then make the mistake.
Looper.prepare() and Looper.loop() just means that this thread can be looped, but I must write while loop or for loop by myself.
This is my code
I want I show the progressdialog until receive the data from server,
and when I got result from the server(if items.size>0), progressdialog will be dismiss;
But I run this code, I receive the data from the server but in runonuiThread, still running progressdialog.
List<ServerData> items = new ArrayList();
progressdialog.show();
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
HttpRequest...
Gson gson = new Gson();
Type type = new TypeToken<List<ServerData>>(){}.getType();
items = gson.fromJson(String,type);
Log.d("ThreadInfo","" + items.size);
runOnUiThread(new Runnable() {
#Override
public void run() {
if(items.size > 0){
progressdialog.dismiss();
}
}
});
}
});
When I Log the ThreadInfo, It sends items.size value is 1
but still progressdialog is running and not disappear.
Question,
How I can dismiss the progressdialog when items.size is not 0?
If you have any idea, help me please.
You have to change your code like this
if (items.size > 0) {
runOnUiThread(new Runnable() {
#Override
public void run() {
progressdialog.dismiss();
}
});
}
I have this below piece of code, I want to my thread to wait untill either of the callback functions are called.
Issue my code hits the line where i am making the synchObj wait but after that it just vanishes it doesn't move anywhere.
If after wait its not gonna move ahead how the notify will be called?
iGPlaceApi.getStreams(new Callback<mGooglePlacesApiResponse>() {
#Override
public void failure(RetrofitError retrofitError) {
String serverResponse = retrofitError.toString();
synchronized (synchObj) {
synchObj.notify();
//synchObj.notifyAll();
}
}
#Override
public void success(mGooglePlacesApiResponse googlePlacesObj, Response arg1){
nearbyPlaces = new String[googlePlacesObj.results.size()][4];
for (int i = 0; i < googlePlacesObj.results.size(); i++) {
mGooglePlaces.place place = googlePlacesObj.results.get(i);
nearbyPlaces[i][0] = place.icon;
nearbyPlaces[i][1] = place.name;
nearbyPlaces[i][2] = String.valueOf(place.geometry.location.lat);
nearbyPlaces[i][3] = String.valueOf(place.geometry.location.lng);
}
synchronized (synchObj) {
synchObj.notify();
//synchObj.notifyAll();
}
}
});
synchronized (synchObj) {
synchObj.wait();
}
Handler handler=new Handler();
Runnable thr = new Runnable() {
public void run() {
iGPlaceApi.getStreams(new Callback<mGooglePlacesApiResponse>() {
#Override
public void failure(RetrofitError retrofitError) {
String serverResponse = retrofitError.toString();
synchronized (synchObj) {
synchObj.notifyAll();
}
}
#Override
public void success(mGooglePlacesApiResponse googlePlacesObj, Response arg1) {
nearbyPlaces = new String[googlePlacesObj.results.size()][4];
for (int i = 0; i < googlePlacesObj.results.size(); i++) {
mGooglePlaces.place place = googlePlacesObj.results.get(i);
nearbyPlaces[i][0] = place.icon;
nearbyPlaces[i][1] = place.name;
nearbyPlaces[i][2] = String.valueOf(place.geometry.location.lat);
nearbyPlaces[i][3] = String.valueOf(place.geometry.location.lng);
}
synchronized (synchObj) {
synchObj.notifyAll();
}
}
});
}
};
handler.post(thr);
synchronized (synchObj) {
synchObj.wait();
}
You can't do that. The callbacks are going to be called on the same thread that is calling the getStreams method.
The callbacks cannot be called until your calling method returns. You probably need to call getStreams in yet another thread.
You are doing it wrong. Handler task will be executed on the same thread so in your case it will never be executed. If you want to wait for your task to be done you should use Thread like this:
// defined somewhere
boolean done = false;
Thread thr=new Thread(Runnable() {
public void run() {
iGPlaceApi.getStreams(new Callback<mGooglePlacesApiResponse>() {
#Override
public void failure(RetrofitError retrofitError) {
String serverResponse = retrofitError.toString();
synchronized (synchObj) {
done = true;
synchObj.notifyAll();
}
}
#Override
public void success(mGooglePlacesApiResponse googlePlacesObj, Response arg1) {
nearbyPlaces = new String[googlePlacesObj.results.size()][4];
for (int i = 0; i < googlePlacesObj.results.size(); i++) {
mGooglePlaces.place place = googlePlacesObj.results.get(i);
nearbyPlaces[i][0] = place.icon;
nearbyPlaces[i][1] = place.name;
nearbyPlaces[i][2] = String.valueOf(place.geometry.location.lat);
nearbyPlaces[i][3] = String.valueOf(place.geometry.location.lng);
}
synchronized (synchObj) {
done = true;
synchObj.notifyAll();
}
}
});
}
});
thr.start();
synchronized (synchObj) {
while (!done) {
synchObj.wait();
}
}
Note that instead of just runnable I'm using Thread, and done variable which indicates task status which is neccesary since wait call can spontaneously wake up and you have to call it again if the task has not been finished yet.
I am trying to create a loop that will update a TextView.
The idea is to create some sort of progress indicator, that will increment the
loading precentge.
This is what I have tried, but the result is that I see only the last update of the loop, so I get "100%" with no incremntation.
runOnUiThread(new Runnable() {
public void run() {
final TextView progesss = (TextView)findViewById(R.id.progress);
for(int k=1 ; k<=100; k++)
{
progesss.setText(String.valueOf(k) + "%");
try {
Thread.sleep(15);
} catch(InterruptedException e) {
}
}
}
});
Any ideas of how to achieve my goal?
Thanks!
Your Runnable blocks the UI thread when doing Thread.sleep. Instead of sleeping, you should post a new Runnable again. Try with something like this:
final Handler handler = new Handler();
handler.post( new Runnable(){
private int k = 0;
public void run() {
final TextView progess = (TextView)findViewById(R.id.progress);
progess.setText(String.valueOf(k) + "%");
k++;
if( k <= 100 )
{
// Here `this` refers to the anonymous `Runnable`
handler.postDelayed(this, 15);
}
}
});
That will give the UI thread a chance to run between each call, letting it do its stuff like handling input and drawing stuff on the screen.
You use background thread and UI Thread.
public class testAsync extends AsyncTask<Void, Integer, Voi>{
TextView progress; // You will set TextView referans
protected void doInBackground(){
for(int k=1 ; k<=100; k++)
{
try {
Thread.sleep(1000);
publishProgress(k);
} catch(InterruptedException e) {}
}
}
protected void onProgressUpdate(Integer.. values)
{
progress.setText(values[0]+");
}
}
}
I just tried to implement a progressdialog and I have some issues to change the text during my long and complex calculations.
for (String aString:myStringArray){
Log.v(TAG, aString);
mProgressDialog.incrementProgressBy(1);
mProgressDialog.setMessage(aString);
}
I can clearly see the incrementProgressBy working and my dialog updating, but the message does not change.
Any idea on how to make that work?
Thank a lot.
Just found the answer, that's working fine:
runOnUiThread(changeMessage);
with that code:
private Runnable changeMessage = new Runnable() {
#Override
public void run() {
//Log.v(TAG, strCharacters);
m_ProgressDialog.setMessage(strCharacters);
}
};
I upload pictures to Firebase in a loop and updating the ProgressDialog each image:
(I am in a Fragment, so I use getActivity() before calling to runOnUiThread())
List<Bitmap> bitmaps;
int picCounter = 1;
...
progressDialog = ProgressDialog.show
(getContext(), "sending...", "just a minute", false, false);
new Thread(new Runnable() {
#Override
public void run() {
for (int i = 0; i < bitmaps.size(); i++) {
String filename = String.valueOf(i);
uploadPic(bitmaps.get(i), "img" + filename, new MyCallback() {
#Override
public void onFinish() {
picCounter++;
Objects.requireNonNull(getActivity()).runOnUiThread(new Runnable() {
public void run() {
progressDialog.setTitle ("upoading " + picCounter + "image from " + bitmaps.size());
}
});
}
});
}
}
}).start();
uploadPic method:
public interface MyCallback { void onFinish ();}
private void uploadPic(final Bitmap bitmap, final String fileName, final MyCallback callback) {
... // uploading to firebase and then:
callback.onFinish();
}